[51nod1587]半现串
将s所有长度为d/2的子串放进ac自动机中,直接匹配就可以判定半现串了
再对其做一个差分,询问一个前缀的半现串个数,在ac自动机上数位dp,f[i][j][0/1]表示走了i步(i位的字符串),走到节点j,是否达到上限的方案数
对于ac自动机上的结束节点,直接重置并累计答案即可
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 1005 4 #define c (s[i]-'0') 5 #define mod 1000000007 6 queue<int>q; 7 int V,n,d,ans,mi[51],mo[51],vis[N*50],nex[N*50],ch[N*50][11],f[51][N*50][2]; 8 char s[N],s1[51],s2[51]; 9 void add(int l,int r){ 10 int k=0; 11 for(int i=l;i<=r;i++){ 12 if (!ch[k][c])ch[k][c]=++V; 13 k=ch[k][c]; 14 } 15 vis[k]=1; 16 } 17 void build(){ 18 for(int i=0;i<10;i++) 19 if (ch[0][i])q.push(ch[0][i]); 20 while (!q.empty()){ 21 int k=q.front(); 22 q.pop(); 23 for(int i=0;i<10;i++) 24 if (!ch[k][i])ch[k][i]=ch[nex[k]][i]; 25 else{ 26 nex[ch[k][i]]=ch[nex[k]][i]; 27 q.push(ch[k][i]); 28 } 29 } 30 } 31 int query(char *s){ 32 ans=0; 33 memset(f,0,sizeof(f)); 34 mi[0]=mo[d]=f[0][0][1]=1; 35 for(int i=1;i<=d;i++)mi[i]=10LL*mi[i-1]%mod; 36 for(int i=d-1;i>=0;i--)mo[i]=(mo[i+1]+1LL*c*mi[d-i-1])%mod; 37 for(int i=0;i<d;i++) 38 for(int j=0;j<=V;j++){ 39 if (vis[j])continue; 40 for(int k=0;k<10;k++){ 41 int p=ch[j][k]; 42 f[i+1][p][0]=(f[i+1][p][0]+f[i][j][0])%mod; 43 if (k<=c)f[i+1][p][k==c]=(f[i+1][p][k==c]+f[i][j][1])%mod; 44 } 45 } 46 for(int i=0;i<=d;i++) 47 for(int j=0;j<=V;j++) 48 if (vis[j]) 49 ans=(ans+1LL*f[i][j][0]*mi[d-i]+1LL*f[i][j][1]*mo[i])%mod; 50 return ans; 51 } 52 int main(){ 53 scanf("%s%s%s",s,s1,s2); 54 n=strlen(s); 55 d=strlen(s1); 56 V=0; 57 for(int i=0;i<=n-d/2;i++)add(i,i+d/2-1); 58 build(); 59 ans=(query(s2)-query(s1)+mod)%mod; 60 for(int i=0,k=0;s1[i];i++){ 61 k=ch[k][s1[i]-'0']; 62 if (vis[k]){ 63 ans=(ans+1)%mod; 64 break; 65 } 66 } 67 printf("%d",ans); 68 }