大意:
求区间 [L,R] 中有长度大于 2 的回文子串的数量。 数据范围:1≤L<R≤10^1000
思路:
对于这种回文串的东西第一眼肯定是想到合并状态。如果正着想的话,显然所有回文串都满足有相邻两位相等或者间隔一位的两个数字相等,但是这样计数的话还要考虑到相等的位置以及第二种情况时中间数字与两边数字是否相等等一系列情况,想想也不是1e1000能解决的
所以可以反过来考虑,那就是统计不含回文字串的个数,不难发现其性质就是每一位都与前两位不同。所以设一个三维的dp,i表示当前位数,j表示前一位,k表示前一位的前一位。做数位dp的话,转移就按照正常的转移来就好了,只是中间遍历时要特判一下前导0时的情况,需要将前两位都置为-1。
最后高精度减法用取模运算来代替即可
思路不是很难,但是写起来还是有些磕磕绊绊的,还是太生了
code
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
const ll N=1e5+10;
const ll mod=1e9+7;
string n,m;
ll dp[1010][15][15];
ll a[1010];
ll cnt=0;
ll gt(string s)
{
ll ans=0;
for(int i=0;i<s.size();++i)
{
ans=(ans*10%mod+(s[i]-'0'))%mod;
}
return ans;
}
ll dfs(ll x,ll head,ll op,ll pre,ll ppre)//head 前导0,op 上限
{
if(x==0) return 1;
if(!head&&!op&&dp[x][pre][ppre]!=-1&&pre!=-1&&ppre!=-1) return dp[x][pre][ppre];
ll tot=0;
ll lim=op?a[x]:9;
for(int i=0;i<=lim;++i)
{
if(i==pre||i==ppre) continue;
if(head&&i==0) tot=(tot+dfs(x-1,1,op&&i==lim,-1,-1))%mod;
// else if(head) tot=(tot+dfs(x-1,0,op&&i==lim,-1,i))%mod;
else tot=(tot+dfs(x-1,0,op&&i==lim,i,pre))%mod;
}
if(!head&&!op&&pre!=-1&&ppre!=-1) dp[x][pre][ppre]=tot;
return tot;
}
ll f(string s,ll opp)
{
memset(dp,-1,sizeof dp);
cnt=0;
for(int i=s.size()-1;i>=0;--i) a[++cnt]=s[i]-'0';
if(opp==1)
{
a[1]--;
for(int i=1;i<=cnt;++i)
{
if(a[i]==-1)
{
a[i]=9;
a[i+1]--;
}
}
}
// for(int i=1;i<=cnt;++i) cout<<a[i];
// cout<<endl;
return dfs(cnt,1,1,-1,-1);
}
void solve()
{
cin>>n>>m;
ll nn=0,mm=0;
ll all=((gt(m)-gt(n)+1)%mod+mod)%mod;
ll det=((f(m,0)-f(n,1))%mod+mod)%mod;
// cout<<det<<endl;
cout<<(((all-det)%mod)+mod)%mod<<endl;
}
int main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
solve();
return 0;
}