洛谷传送门
典型的数位dp。
题目要求求区间 \([l, r]\) 内有多少个数至少有长度为 2 的回文串。
不难发现,我们只需要考虑长度为 2 或 3 的回文串即可。
所以我们记忆化搜索时,存一下当前数的前一个数,和前前个数,判断一下即可。
具体看代码吧,感觉挺好理解的。
#include <iostream> #include <cstdio> #include <cstring> #define N 1010 #define ll long long using namespace std; const ll mod = 1e9 + 7; ll l1, l2, l, r; ll num[N], dp[N][10][10]; char s1[N], s2[N]; void init(){ scanf("%s%s", s1 + 1, s2 + 1); l1 = strlen(s1 + 1); l2 = strlen(s2 + 1); for(ll i = 1; i <= l1; i++) l = ((l * 10) % mod + s1[i] - '0') % mod; for(ll i = 1; i <= l2; i++) r = ((r * 10) % mod + s2[i] - '0') % mod; return; } ll dfs(ll len, ll a, ll b, ll flag, ll lim){ //a 表示前一位, b 表示前前位, flag 判断前导0, lim 判断前一位是否到最高位 if(!len) return 1; if(!flag && !lim && a != -1 && b != -1 && dp[len][a][b] != -1) return dp[len][a][b]; ll x = lim ? num[len] : 9; ll res = 0; for(ll i = 0; i <= x; i++){ if(i == a || i == b) continue; if(flag && !i) res = (res + dfs(len - 1, -1, -1, 1, lim && (i == x))) % mod;//是前导 0 的话,a 和 b 都赋值为 -1 else res = (res + dfs(len - 1, i, a, 0, lim && (i == x))) % mod; } if (!flag && !lim && a != -1 && b != -1) dp[len][a][b] = res; return res; } ll solve(){ memset(dp, -1, sizeof(dp)); for(ll i = 1; i <= l1; i++) num[l1 - i + 1] = s1[i] - '0'; ll ans1 = dfs(l1, -1, -1, 1, 1);//计算的是 1 ~ l 中非萌数个数 ans1--;//假设 l 是萌数,1 ~ l-1 中非萌数个数 -1 for(ll i = 1; i <= l2; i++) num[l2 - i + 1] = s2[i] - '0'; ll ans2 = dfs(l2, -1, -1, 1, 1);//此时 ans 为 l+1 ~ r 中非萌数个数 for(int i = 2; i <= l1; i++)//判断 l 是否为萌数,若 l 为萌数,则补回来,暴力判断 if(s1[i] == s1[i - 1] || (s1[i] == s1[i - 2] && i - 2 >= 1)){ ans1++; break; } return ans2 - ans1; } int main(){ init(); printf("%lld\n", ((r - l - solve() + 1) % mod + mod) % mod);//前缀和思想 }