Description
有两个串S,T。T的长度是d。我们说T在S中半现的条件是当T的某一个长度为 ⌊d/2⌋ 的子串是S的一个子串。(子串是在原串中连续出现的一段字符串)。
现在给定一个原串s,另外给出x,y,长度为d他们都只包含数字字符,问区间[x,y]中在s中半现的数字有多少个。
答案比较大,对 109+7 取余后输出。
1≤|s|≤1000,2≤d<=50
Solution
区间[x,y]的答案可以转化成[1,y]的减去[1,x-1]的
因为原串长度只有1000,那么考虑S所有长度为d/2的子串都扔到AC自动机里去
然后就可以按位DP了
设F[i][j][0,1]表示当前匹配到第i位,在AC自动机上走到j这个节点,当前是否顶住上限
当j这个节点所代表的字符串长度已经达到了d/2,那么计入答案,并将这个状态清空
小优化是当前状态如果是0就不转移
Code
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <iostream>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define N 50005
#define M 2005
#define mo 1000000007
#define LL long long
using namespace std;
char st[M],ls[51],rs[51];
int m,n,n1,t[N][10],fail[N],le[N],ed[N],d[N],m1;
LL f[51][N][2],cf[M],sq[51];
void make()
{
d[1]=1;
int l=0,r=1;
while(l<r)
{
int k=d[++l];
fo(i,0,9)
{
if(t[k][i])
{
d[++r]=t[k][i];
if(k==1) fail[t[k][i]]=k;
else
{
int p=fail[k];
while(p>1&&!t[p][i]) p=fail[p];
if(t[p][i]) p=t[p][i];
fail[t[k][i]]=p;
}
}
}
}
}
LL ksm(LL k,LL n)
{
LL s=1;
for(;n;k=k*k%mo,n>>=1) if(n&1) s=s*k%mo;
return s;
}
LL get(char *st,int m)
{
LL ans=0;
sq[m+1]=1;
sq[m]=st[m]-'0'+1;
fod(i,m-1,1) sq[i]=(sq[i+1]+((LL)(st[i]-'0'))*ksm(10,m-i)%mo)%mo;
memset(f,0,sizeof(f));
f[0][1][1]=1;
fo(i,0,m)
{
fo(j,1,n1)
{
if(le[j]==m1/2)
{
(ans+=f[i][j][0]*ksm(10,m-i)%mo)%=mo,f[i][j][0]=0;
(ans+=f[i][j][1]*sq[i+1]%mo)%=mo,f[i][j][1]=0;
}
if(i<m&&f[i][j][0]+f[i][j][1]>0)
fo(c,0,9)
{
int p=j;
while(p>1&&!t[p][c]) p=fail[p];
if(t[p][c]) p=t[p][c];
(f[i+1][p][0]+=f[i][j][0])%=mo;
if(c<=st[i+1]-'0')
{
if(c==st[i+1]-'0') (f[i+1][p][1]+=f[i][j][1])%=mo;
else (f[i+1][p][0]+=f[i][j][1])%=mo;
}
}
}
}
return ans;
}
int main()
{
scanf("%s",st+1);
n=strlen(st+1);
scanf("\n%s",ls+1);
scanf("\n%s",rs+1);
m=strlen(ls+1);
int j=m;
m1=m;
while(ls[j]=='0') ls[j]='9',j--;
ls[j]=ls[j]-1;
n1=1;
fo(i,1,n-m1/2+1)
{
int k=1;
fo(j,1,m1/2)
{
int c=st[i+j-1]-'0';
if(!t[k][c]) t[k][c]=++n1,le[n1]=j;
k=t[k][c];
}
ed[k]=1;
}
make();
printf("%lld\n",(get(rs,m1)-get(ls,m)+mo)%mo);
}