题目
https://www.luogu.com.cn/problem/P3413
思路
不要被“回文”这个东西吓到了。考虑一个任意长度\(\geq 2\)的回文串,它必然包含一个长度为3或长度为2的回文子串。也就是说,只要串中存在\(S_{i}\)满足\(S_{i}==S_{i-1}\)或\(S_{i}==S_{i-2}\),那么这个数就是萌数.
接下来就是数位DP的常规套路了,设\(f(x)\)为\(1\)到\(x\)中萌数的个数,最终答案就是\(f(r)-f(l-1)\)。考虑到本题\(l\)和\(r\)只能以字符串形式存储,为了避免高精度减法,我们把答案换成\(f(r)-f(l)\),如果\(l\)自身是萌数,则答案加一。(这个直接暴力检验)。
最重要的就是数位DP的主过程了。
从高位向低位处理。
ll dfs(char *x,int digit,int limit,int k/*该数位前有多少个非前导零的位*/)
我的dfs过程是这么写的,其中\(x\)是字符数组,表示传进去的\(l\)或\(r\),digit表示当前处理的是第几位(为了方便计算,这里指权值为\(10^{digit}\)的那一位),\(limit\)表示之前取的数是否是上限(这一点可能说的不是很清楚,可以看其他大佬的数位DP博客,\(limit\)基本都有相同的含义),\(k\)表示我们当前构造的萌数已经有了多少位(其实就是用来特判开头两个数字的,因为他们的\(digit-2\)不存在)。
设当前位为\(S_{digit}\),那么我们考虑它能否与\(S_{digit-1}\)或\(S_{digit-2}\)相同,针对\(limit\)和\(k\)的不同取值做不同的决策(这部分详见注释)。
分类讨论写的比较丑,大佬轻喷QWQ。
代码
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define mod (int)(1e9+7)
#define ll long long
#define maxn 1010
using namespace std;
char l[maxn],r[maxn];
ll pow_10[maxn],suf[maxn];//预处理10的次幂及l和r的后缀
ll dp[maxn][10][10];
int LEN;
ll dfs(char *x,int digit,int limit,int k/*该数位前有多少个非前导零的位*/){
ll sum=0;
if(digit<0) return 0;
if(k==0){
if(limit){//有限制
sum+=(x[digit]-'0'-1)*dfs(x,digit-1,0,1)%mod;sum%=mod;//当前位不取0也不取到x[i],之后的选择不受限
sum+=dfs(x,digit-1,1,1);sum%=mod;//当前位取到x[i],这将使之后的选择仍然受限
sum+=dfs(x,digit-1,0,0);sum%=mod;//当前位空出,继续填0,之后的选择不受限
}
else{//无限制
sum+=9*dfs(x,digit-1,0,1)%mod;sum%=mod;//当前填1~9,由于之前的填法已经使构造的值必然小于x,所以之后当然也没有限制
sum+=dfs(x,digit-1,0,0);sum%=mod;//当前填0
}
}
else if(k==1){
if(limit){//有限制
if(x[digit]>x[digit+1]){//x的当前位大于前一位,这使我们可以令这一位=x[digit+1],从而满足萌数条件
sum+=(x[digit]-'0'-1)*dfs(x,digit-1,0,2)%mod;sum%=mod;//平凡值,不满足萌数也不是上限,取法有(x[digit]-'0'-1)种
sum+=dfs(x,digit-1,1,2);sum%=mod;//取上限,之后受限
sum+=pow_10[digit];sum%=mod;//取x[digit+1],使满足萌数条件,后面随便取,总计pow_10[digit](后面digit个位置每个有10种取法)
}
else if(x[digit]==x[digit+1]){//取上限时正好满足萌数条件,与大于的情况有一些微妙不同
sum+=(x[digit]-'0')*dfs(x,digit-1,0,2)%mod;sum%=mod;
sum+=suf[digit]+1;sum%=mod;//这里,虽然已经满足萌数条件,但此时若任意取有可能导致超出上界,所以不是10的次幂而是x的后缀
}
else{//该位已经无法满足萌数条件了
sum+=(x[digit]-'0')*dfs(x,digit-1,0,2)%mod;sum%=mod;
sum+=dfs(x,digit-1,1,2);sum%=mod;
}
}
else{//无限制
sum+=9*dfs(x,digit-1,0,2)%mod;sum%=mod;//取不等于前一位的值,有⑨种取法
sum+=pow_10[digit];sum%=mod;//取等于前一位的数字,满足萌数条件
}
}
else{//k>=2,与k==1相比无非多了一个与digit-2位的比较,只不过计数麻烦一点,注意细节
if(limit){
int c=0;
if(x[digit+1]<x[digit]){
c++;
sum+=pow_10[digit];sum%=mod;
}
if(x[digit+1]==x[digit]){
sum+=suf[digit]+1;sum%=mod;
}
if(x[digit+2]<x[digit]){
c++;
sum+=pow_10[digit];sum%=mod;
}
if(x[digit+2]==x[digit]){
sum+=suf[digit]+1;sum%=mod;
}
sum+=(x[digit]-'0'-c)*dfs(x,digit-1,0,k+1)%mod;sum%=mod;
if(x[digit]!=x[digit+1]&&x[digit]!=x[digit+2]){
sum+=dfs(x,digit-1,1,k+1);sum%=mod;
}
}
else{
sum+=8*dfs(x,digit-1,0,k+1)%mod;sum%=mod;//与之前选择填上digit+1位,digit+2位均不同
sum+=2*pow_10[digit]%mod;sum%=mod;//与其中之一相同
//假设我们正在构造的萌数候选用y数组表示,值得注意的是y[digit+1]和y[digit+2]保证不同,否则我们早就因为满足萌数条件而直接计算出值了,不会走到这一步。
//所以这里的8和2是确定的
}
}
return sum;
}
ll f(char *s){
LEN=strlen(s);
for(int i=1;i<LEN;i++){
suf[i]=suf[i-1]+(s[i-1]-'0')*pow_10[i-1];
}//简简单单的预处理
return dfs(s,LEN-1,1,0);
}
void rev(char *s){
int n=strlen(s);
for(int i=0;2*i<n-1;++i) swap(s[i],s[n-1-i]);
}
int main(){
int i,flag=0,n;
ll ans;
scanf("%s%s",l,r);
rev(l);rev(r);
pow_10[0]=1;
for(i=1;i<=1000;i++)
pow_10[i]=pow_10[i-1]*10%mod;
ans=f(r)-f(l);
n=strlen(l);
if(l[0]==l[1]) flag=1;
for(i=2;i<n;i++){
if(l[i]==l[i-1]||l[i]==l[i-2]){
flag=1;
break;
}
}
if(flag) ans++;
ans=(ans+mod)%mod;
// printf("%lld %lld\n",f(l),f(r));
printf("%lld",ans);
// system("pause");
return 0;
}