1. 程式人生 > 其它 >Codeforces 1466 G. Song of the Sirens —— kmp,想法,有丶東西

Codeforces 1466 G. Song of the Sirens —— kmp,想法,有丶東西

技術標籤:想法字串

This way

題意:

給你串s0和t,t的長度為n。si的構造方法如下: s i = s i − 1 t [ i ] s i − 1 s_i=s_{i-1}t[i]s_{i-1} si=si1t[i]si1
有1
個詢問,問你 s k s_k sk中有多少個w

題解:

首先這個構造方法就能看出來不能直接暴力地去做。那麼我們找到第一個 s i s_i si使得 ∣ s i ∣ > ∣ w ∣ |s_i|>|w| si>w。可以發現,si在之後出現的次數每次都是*2.那麼我們只需要找到si中有多少個w即可。那麼對於i+1~n的情況該怎麼做,

我們可以發現, s j = s j − 1 C 1 s j − 1 , s k = s k − 1 C 2 s k − 1 , s_j=s_{j-1}C_1s_{j-1},s_k=s_{k-1}C_2s_{k-1}, sj=sj1C1sj1,sk=sk1C2sk1 j , k > i , C 1 = = C 2 j,k>i,C_1==C_2 j,k>iC1==C2的時候,串w的出現次數增加數量是相同的,
num[i][j]表示字元i到了第j個串的時候出現的次數,這樣預處理出來所有的情況數。
然後我們對於w找到每個位置是否能作為t串增加的那一個字元,也就是處理出每個位置是否能作為si的前後綴。用兩次kmp即可
在這裡插入圖片描述

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll mod=1e9+7;
const int N=2e6+5;
vector<string>tmp;
string s,t,ss;
int n,q;
ll num[26][N/20+5],p[N/20+5];
void init(){
    int len=s.length(),now=0;
    ss=s;
    while(len<=N&&now<=n){
        tmp.push_back
(ss); ss=ss+t[now++]+ss; len=len*2+1; } for(int i=0;i<26;i++){ for(int j=1;j<=n;j++) num[i][j]=(num[i][j-1]*2+(t[j-1]-'a'==i))%mod; } p[0]=1; for(int i=1;i<=n;i++)p[i]=p[i-1]*2%mod; } int f[N*2]; void kmp(string &st){ f[0]=-1,f[1]=0; int len=st.length(); int i=1,j=0; while(i<len&&j<len){ if(j==-1||st[i]==st[j])f[++i]=++j; else j=f[j]; } } string w,pre,suf; bool can[2][N/2+5]; void cal(int id,int now){ int len=w.length(); memset(can[id],0,sizeof(bool)*(len+1)); while(now>=0)can[id][now]=1,now=f[now]; if(now>=0)can[id][now]=1; } int main() { ios::sync_with_stdio(0); cin.tie(0); cin>>n>>q>>s>>t; init(); while(q--){ int k; cin>>k>>w; int now=0,wlen=w.length(); while(now<k&&tmp[now].size()<=wlen)now++; if(tmp[now].size()<wlen){ printf("0\n"); continue; } ll ans=0; pre=w+"#"+tmp[now]; kmp(pre); int len=pre.length(); cal(0,f[len]); for(int i=wlen;i<=len;i++) ans+=f[i]==wlen; ans=ans*p[k-now]%mod; suf=tmp[now]+"#"+w; kmp(suf); cal(1,f[len]); for(int i=0;i<wlen;i++){ if(!can[0][i]||!can[1][wlen-i-1])continue; int v=w[i]-'a'; ans=(ans+num[v][k]-num[v][now]*p[k-now])%mod; if(ans<0)ans+=mod; } printf("%lld\n",ans); } return 0; }