Description
如果一个字符串从后往前读与从前往后读一致,我们则称之为回文字符串。当一个数字不包含长度大于1的子回文数字时称为非回文数字。例如,16276是非回文数字,但17276不是,因为它包含回文数字727。
你的任务是在一个给定的范围内计算非回文数字的总数。
你的任务是在一个给定的范围内计算非回文数字的总数。
Input
输入仅一行,包含两个整数a和b。
Output
输出仅一行,包含一个整数,表示a到b范围内(包括a和b)非回文数字的总数。
Sample Input
输入1:
123 321
输入2:
123456789 987654321
Sample Output
输出1:
153
输出2:
167386971
Data Constraint
25%的数据:b-a<=100 000.
100%的数据:0<=a<=b<=10^18
100%的数据:0<=a<=b<=10^18
分析
带限制求两数中总数,较显然是数位DP
我们考虑,如果第i位与其i-1位和i-2位都不相同,那么这个数是非回文子树
那么我们设f[i][j][k][l]表示做到第i位,i-2位为j,i-1位为k,是否在边界上
但是我们必须注意特殊的状况:前导零
那么我们设多两位状态[zero2][zero1]表示i-2位是否前导零,i-1位是否前导零,对于前导零不需要进行回文的判断
数位DP的递推好恶心啊
记忆化搜索即可
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include <iostream> #include <cstdio> #include <memory.h> using namespace std; typedef long long ll; ll a,ans; ll f[20][10][10][2][2][2]; int c[20],len; ll Calc(int dep,int pre2,int pre1,bool zero2,bool zero1,bool limit) { if (f[dep][pre2][pre1][zero2][zero1][limit]>=0) return f[dep][pre2][pre1][zero2][zero1][limit]; ll ans=0; if (dep>len) return 1; if (limit) { for (int i=0;i<=c[dep];i++) if ((zero2||i!=pre2)&&(zero1||i!=pre1)) ans+=Calc(dep+1,pre1,i,zero1,zero1&&(i==0),i==c[dep]); } else { for (int i=0;i<=9;i++) if ((zero2||i!=pre2)&&(zero1||i!=pre1)) ans+=Calc(dep+1,pre1,i,zero1,zero1&&(i==0),0); } return f[dep][pre2][pre1][zero2][zero1][limit]=ans; } ll Solve(ll x) { if (x<0) return 0; len=0;memset(f,-1,sizeof f); while (x) c[++len]=x%10,x/=10; for (int i=1;i<=len/2;i++) swap(c[i],c[len-i+1]); return Calc(1,0,0,1,1,1); } int main() { scanf("%lld",&a);a--; ans-=Solve(a); scanf("%lld",&a); ans+=Solve(a); printf("%lld",ans); }