Frequency of String CodeForces - 963D
http://codeforces.com/contest/963/problem/D
题解:https://www.cnblogs.com/Blue233333/p/8881614.html
记M为n个串的总长,L为s的长度
询问串的不同的长度只会有sqrt(M)级别个
(最差的情况是串长为1,2,3,...,x,此时M=(x+1)x/2,因此x是sqrt(M)级别的)
s的子串中,长度为特定值k的子串个数是L级别的,
由于各个字符串互不相同,这就相当于n个串中所有长度为k的串,分别计算出它们在s中出现次数,各个计算结果的和是L级别的
因此只要设计对于每一个询问串都是O(串长+出现次数)的算法就行了
花了好长时间写了个后缀自动机字符串匹配啊。。。。莫不是搞复杂了
代码1.(using连用多个是c++17的,曾经还CE了)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<set> 5 #include<queue> 6 using namespace std; 7 typedef long long LL; 8 char s[100010],ss[100100]; 9 int ll,x[100100],l2; 10 int pp[100100]; 11 vector<int> vv[200100]; 12 set<int> sss[200100]; 13 int n,ans[100100]; 14 int lll[100100]; 15 namespace SAM 16 { 17 int mem,np,root; 18 int len[200100],par[200100]; 19 int trans[200100][26]; 20 int posl[200100],posr[200100]; 21 void append(int ch) 22 { 23 int p=np;np=++mem;len[np]=len[p]+1; 24 for(;p&&!trans[p][ch];p=par[p]) trans[p][ch]=np; 25 if(!p) par[np]=root; 26 else 27 { 28 int q=trans[p][ch]; 29 if(len[q]==len[p]+1) par[np]=q; 30 else 31 { 32 int nq=++mem;par[nq]=par[q];par[q]=par[np]=nq; 33 memcpy(trans[nq],trans[q],sizeof(trans[nq]));len[nq]=len[p]+1; 34 for(;p&&trans[p][ch]==q;p=par[p]) trans[p][ch]=nq; 35 } 36 } 37 } 38 void build() 39 { 40 np=root=++mem; 41 int i,now; 42 for(i=1;i<=ll;i++) append(s[i]-'a'),pp[i]=np,sss[np].insert(i); 43 for(i=1;i<=ll;i++) 44 { 45 for(now=pp[i];now!=root&&!posr[now]&&!posl[now];now=par[now]) 46 { 47 posl[now]=i-len[par[now]];posr[now]=i-len[now]+1; 48 } 49 } 50 } 51 } 52 namespace ST 53 { 54 int ch[200100][26]; 55 using SAM::par; 56 void build() 57 { 58 int i; 59 for(i=1;i<=SAM::mem;i++) 60 { 61 if(par[i]) 62 { 63 ch[par[i]][s[SAM::posl[i]]-'a']=i; 64 } 65 } 66 } 67 /* 68 void out() 69 { 70 int i,j,k;using SAM::len,SAM::posl,SAM::posr; 71 for(i=1;i<=SAM::mem;i++) 72 { 73 for(j=0;j<26;j++) 74 { 75 if(ch[i][j]) 76 { 77 printf("%d %d ",i,ch[i][j]); 78 for(k=posl[ch[i][j]];k>=posr[ch[i][j]];k--) putchar(s[k]); 79 puts(""); 80 } 81 } 82 } 83 } 84 */ 85 } 86 void work(int tt) 87 { 88 using SAM::root,SAM::posl,SAM::posr,ST::ch; 89 int i,now=root,j,k; 90 for(i=l2;i>=1;) 91 { 92 now=ch[now][ss[i]-'a']; 93 if(!now) return; 94 for(j=i,k=posl[now];j>=1&&k>=posr[now];j--,k--) 95 if(ss[j]!=s[k]) 96 return; 97 i-=posl[now]-posr[now]+1; 98 //printf("b%d %d %d %d\n",now,i,posl[now],posr[now]); 99 } 100 vv[now].push_back(tt);//printf("a%d\n",now); 101 } 102 queue<int> q;int in[200100]; 103 void work2() 104 { 105 using SAM::mem,SAM::par; 106 int i,j,t,l,r;vector<int> tmp; 107 for(i=1;i<=mem;i++) if(par[i]) in[par[i]]++; 108 for(i=1;i<=mem;i++) if(!in[i]) q.push(i); 109 while(!q.empty()) 110 { 111 t=q.front();q.pop(); 112 if(vv[t].size()) 113 { 114 tmp.clear(); 115 for(auto k:sss[t]) tmp.push_back(k); 116 //for(i=0;i<tmp.size();i++) printf("%d ",tmp[i]);puts(""); 117 } 118 for(auto p:vv[t]) 119 { 120 for(i=0,j=x[p]-1;j<tmp.size();i++,j++) 121 { 122 ans[p]=min(ans[p],tmp[j]-tmp[i]+lll[p]); 123 } 124 } 125 if(par[t]) 126 { 127 if(sss[par[t]].size()<sss[t].size()) swap(sss[par[t]],sss[t]); 128 for(auto k:sss[t]) sss[par[t]].insert(k); 129 sss[t].clear(); 130 in[par[t]]--; 131 if(!in[par[t]]) q.push(par[t]); 132 } 133 } 134 } 135 int main() 136 { 137 int i; 138 scanf("%s",s+1);ll=strlen(s+1); 139 SAM::build();ST::build(); 140 //ST::out();return 0; 141 scanf("%d",&n); 142 for(i=1;i<=n;i++) 143 { 144 scanf("%d%s",&x[i],ss+1);l2=lll[i]=strlen(ss+1); 145 work(i); 146 } 147 memset(ans,0x3f,sizeof(ans)); 148 work2(); 149 for(i=1;i<=n;i++) 150 printf("%d\n",ans[i]==0x3f3f3f3f?-1:ans[i]); 151 return 0; 152 }
代码2:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<set> 5 #include<queue> 6 using namespace std; 7 typedef long long LL; 8 char s[100010],ss[100100]; 9 int ll,x[100100],l2; 10 int pp[100100]; 11 vector<int> vv[200100]; 12 set<int> sss[200100]; 13 int n,ans[100100]; 14 int lll[100100]; 15 namespace SAM 16 { 17 int mem,np,root; 18 int len[200100],par[200100]; 19 int trans[200100][26]; 20 int posl[200100],posr[200100]; 21 void append(int ch) 22 { 23 int p=np;np=++mem;len[np]=len[p]+1; 24 for(;p&&!trans[p][ch];p=par[p]) trans[p][ch]=np; 25 if(!p) par[np]=root; 26 else 27 { 28 int q=trans[p][ch]; 29 if(len[q]==len[p]+1) par[np]=q; 30 else 31 { 32 int nq=++mem;par[nq]=par[q];par[q]=par[np]=nq; 33 memcpy(trans[nq],trans[q],sizeof(trans[nq]));len[nq]=len[p]+1; 34 posl[nq]=posl[q];posr[nq]=posl[q]-(len[nq]-len[par[nq]])+1;posl[q]=posr[nq]-1; 35 for(;p&&trans[p][ch]==q;p=par[p]) trans[p][ch]=nq; 36 } 37 } 38 } 39 void build() 40 { 41 np=root=++mem; 42 int i,now; 43 for(i=1;i<=ll;i++) 44 { 45 append(s[i]-'a'),sss[np].insert(i); 46 for(now=np;now!=root&&!posr[now]&&!posl[now];now=par[now]) 47 { 48 posl[now]=i-len[par[now]];posr[now]=i-len[now]+1; 49 } 50 } 51 } 52 } 53 namespace ST 54 { 55 int ch[200100][26]; 56 using SAM::par; 57 void build() 58 { 59 int i; 60 for(i=1;i<=SAM::mem;i++) 61 { 62 if(par[i]) 63 { 64 ch[par[i]][s[SAM::posl[i]]-'a']=i; 65 } 66 } 67 } 68 /* 69 void out() 70 { 71 int i,j,k;using SAM::len,SAM::posl,SAM::posr; 72 for(i=1;i<=SAM::mem;i++) 73 { 74 for(j=0;j<26;j++) 75 { 76 if(ch[i][j]) 77 { 78 printf("%d %d ",i,ch[i][j]); 79 for(k=posl[ch[i][j]];k>=posr[ch[i][j]];k--) putchar(s[k]); 80 puts(""); 81 } 82 } 83 } 84 } 85 */ 86 } 87 void work(int tt) 88 { 89 using SAM::root,SAM::posl,SAM::posr,ST::ch; 90 int i,now=root,j,k; 91 for(i=l2;i>=1;) 92 { 93 now=ch[now][ss[i]-'a']; 94 if(!now) return; 95 for(j=i,k=posl[now];j>=1&&k>=posr[now];j--,k--) 96 if(ss[j]!=s[k]) 97 return; 98 i-=posl[now]-posr[now]+1; 99 //printf("b%d %d %d %d\n",now,i,posl[now],posr[now]); 100 } 101 vv[now].push_back(tt);//printf("a%d\n",now); 102 } 103 queue<int> q;int in[200100]; 104 void work2() 105 { 106 using SAM::mem,SAM::par; 107 int i,j,t;vector<int> tmp; 108 for(i=1;i<=mem;i++) if(par[i]) in[par[i]]++; 109 for(i=1;i<=mem;i++) if(!in[i]) q.push(i); 110 while(!q.empty()) 111 { 112 t=q.front();q.pop(); 113 if(vv[t].size()) 114 { 115 tmp.clear(); 116 for(auto k:sss[t]) tmp.push_back(k); 117 //for(i=0;i<tmp.size();i++) printf("%d ",tmp[i]);puts(""); 118 } 119 for(auto p:vv[t]) 120 { 121 for(i=0,j=x[p]-1;j<tmp.size();i++,j++) 122 { 123 ans[p]=min(ans[p],tmp[j]-tmp[i]+lll[p]); 124 } 125 } 126 if(par[t]) 127 { 128 if(sss[par[t]].size()<sss[t].size()) swap(sss[par[t]],sss[t]); 129 for(auto k:sss[t]) sss[par[t]].insert(k); 130 sss[t].clear(); 131 in[par[t]]--; 132 if(!in[par[t]]) q.push(par[t]); 133 } 134 } 135 } 136 int main() 137 { 138 int i; 139 scanf("%s",s+1);ll=strlen(s+1); 140 SAM::build();ST::build(); 141 //ST::out();return 0; 142 scanf("%d",&n); 143 for(i=1;i<=n;i++) 144 { 145 scanf("%d%s",&x[i],ss+1);l2=lll[i]=strlen(ss+1); 146 work(i); 147 } 148 memset(ans,0x3f,sizeof(ans)); 149 work2(); 150 for(i=1;i<=n;i++) 151 printf("%d\n",ans[i]==0x3f3f3f3f?-1:ans[i]); 152 return 0; 153 }
还有一份看上去很高妙的bitset字符串匹配(不是自己写的,先记一下)
来源:http://codeforces.com/contest/963/submission/37784765
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 #define N 100005 3 using namespace std; 4 bitset<N> b[26],tmp; 5 char s[N],t[N]; 6 int main(){ 7 scanf("%s",s); 8 int n=strlen(s); 9 for (int i=0;i<n;i++) 10 b[s[i]-'a'][i]=1; 11 int Q; scanf("%d",&Q); 12 while (Q--){ 13 int k; scanf("%d%s",&k,t); 14 int m=strlen(t); 15 tmp.set(); 16 for (int i=0;i<m;i++) 17 tmp&=b[t[i]-'a']>>i; 18 if (tmp.count()<k){ 19 puts("-1"); 20 continue; 21 } 22 vector<int> v; 23 for (int i=tmp._Find_first();i<n;i=tmp._Find_next(i)) 24 v.push_back(i); 25 int ans=1e9; 26 for (int i=0;i+k-1<v.size();i++) 27 ans=min(ans,v[i+k-1]-v[i]+m); 28 printf("%d\n",ans); 29 } 30 }
就是搞复杂了。。
要找一个字符串,只要在后缀自动机上一位一位找过去,保证最后到达的一定也是后缀树上的对应节点。。。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #pragma GCC diagnostic error "-std=c++11" 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 #include<set> 6 #include<queue> 7 using namespace std; 8 typedef long long LL; 9 char s[100010],ss[100100]; 10 int ll,x[100100],l2; 11 int pp[100100]; 12 vector<int> vv[200100]; 13 set<int> sss[200100]; 14 int n,ans[100100]; 15 int lll[100100]; 16 int teststet; 17 int ssss; 18 19 namespace SAM 20 { 21 int mem,np,root; 22 int len[200100],par[200100]; 23 int trans[200100][26]; 24 void append(int ch) 25 { 26 int p=np;np=++mem;len[np]=len[p]+1; 27 for(;p&&!trans[p][ch];p=par[p]) trans[p][ch]=np; 28 if(!p) par[np]=root; 29 else 30 { 31 int q=trans[p][ch]; 32 if(len[q]==len[p]+1) par[np]=q; 33 else 34 { 35 int nq=++mem;par[nq]=par[q];par[q]=par[np]=nq; 36 memcpy(trans[nq],trans[q],sizeof(trans[nq]));len[nq]=len[p]+1; 37 for(;p&&trans[p][ch]==q;p=par[p]) trans[p][ch]=nq; 38 } 39 } 40 } 41 void build() 42 { 43 np=root=++mem; 44 int i; 45 for(i=1;i<=ll;i++) 46 { 47 append(s[i]-'a'),sss[np].insert(i); 48 } 49 } 50 } 51 void work(int tt) 52 { 53 using SAM::trans; 54 int i,now=SAM::root; 55 for(i=1;i<=l2;i++) 56 { 57 now=trans[now][ss[i]-'a']; 58 } 59 vv[now].push_back(tt); 60 } 61 queue<int> q;int in[200100]; 62 void work2() 63 { 64 using SAM::mem;using SAM::par; 65 int i,j,t;vector<int> tmp; 66 for(i=1;i<=mem;i++) if(par[i]) in[par[i]]++; 67 for(i=1;i<=mem;i++) if(!in[i]) q.push(i); 68 while(!q.empty()) 69 { 70 t=q.front();q.pop(); 71 if(vv[t].size()) 72 { 73 tmp.clear(); 74 for(auto k:sss[t]) tmp.push_back(k); 75 } 76 for(auto p:vv[t]) 77 { 78 for(i=0,j=x[p]-1;j<tmp.size();i++,j++) 79 { 80 ans[p]=min(ans[p],tmp[j]-tmp[i]+lll[p]); 81 } 82 } 83 if(par[t]) 84 { 85 if(sss[par[t]].size()<sss[t].size()) swap(sss[par[t]],sss[t]); 86 for(auto k:sss[t]) sss[par[t]].insert(k); 87 sss[t].clear(); 88 in[par[t]]--; 89 if(!in[par[t]]) q.push(par[t]); 90 } 91 } 92 } 93 int main() 94 { 95 int i; 96 scanf("%s",s+1);ll=strlen(s+1); 97 SAM::build(); 98 scanf("%d",&n); 99 for(i=1;i<=n;i++) 100 { 101 scanf("%d%s",&x[i],ss+1);l2=lll[i]=strlen(ss+1); 102 work(i); 103 } 104 int aefsaf; 105 memset(ans,0x3f,sizeof(ans)); 106 work2(); 107 for(i=1;i<=n;i++) 108 printf("%d\n",ans[i]==0x3f3f3f3f?-1:ans[i]); 109 return 0; 110 }