题意:n个长度为k的字符串,首尾相接,按顺时针顺序写在一张CD上,每个字符串只写一次总字符串长度小于1e6。保证每个字符串不同。这样CD上就有了一个环形字符串,长度是n*k。又给出g个长度为k的字符串列表(编号从1到g),按照顺时针打印CD上字符串的编号。保证每个字符串不同。保证这g个字符串长度的和小于2*1e6。
下面代码和思路参考了Wannafly每日一题,顺便学会了Hash。记录了模板。
下面代码和思路参考了Wannafly每日一题,顺便学会了Hash。记录了模板。
解题思路:可以发现,如果枚举第一个串的开始位置,由于输入的g个串,长度都为k,那么每个串的位置就固定了。那么我只要知道,在主串上那一段位置的字符串,是否存在在g个串中,然后如果每个都存在,那么就是符合的。这一部分可以用字符串hash做到O(1)判断。复杂度的话,枚举第一个串的复杂度是k,一共需要匹配n个字符串,所以总复杂度是O(nk)。要注意不能用自然溢出写,因为可以造出数据卡掉自然溢出。
转:http://blog.csdn.net/just_sort/article/details/54425377
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N = 2e6+5;
const int seed=131;
int mod[2]={1000000007,1000000009};
char S[N];
int F[2][N];
int pre[2][N];
int n,g,w;
int tim,vis[N],ans[N];
map<pii,int> cnt;
void pre_init()
{
for(int t=0;t<=1;t++)
{
F[t][0]=1;
for(int i=1;i<N;i++)
F[t][i]=(ll)F[t][i-1]*seed%mod[t];
}
}
pii Hash(string s){
int ret[2]={0};
int len=s.size();
for(int t=0;t<=1;t++)
for(int i=0;i<len;i++)
ret[t]=((ll)ret[t]*seed+s[i])%mod[t];
return pii(ret[0],ret[1]);
}
void pre_solve()
{
int len=strlen(S+1);
for(int t=0;t<=1;t++)
{
pre[t][0]=0;
for(int i=1;i<=len;i++)
pre[t][i]=((ll)pre[t][i-1]*seed+S[i])%mod[t];
for(int j=len+1,i=1;i<=w-1;i++,j++)
pre[t][j]=((ll)pre[t][j-1]*seed+S[i])%mod[t];
}
}
pii Hash(int l,int r)
{
int ret[2];
for(int t=0;t<=1;t++)
ret[t]=(pre[t][r]-(ll)pre[t][l-1]*F[t][r-l+1]%mod[t]+mod[t])%mod[t];
return pii(ret[0],ret[1]);
}
bool check(int id){
tim++;
int l=id,r=id+w-1;
for(int i=1;i<=n;i++,l+=w,r+=w)
{
pii t=Hash(l,r);
if(!cnt.count(t)) return 0;
int p=cnt[t];
if(vis[p]==tim) return 0;
vis[p]=tim;ans[i]=p;
}
printf("YES\n");
for(int i=1;i<=n;i++)
printf("%d ",ans[i] );
return 1;
}
int main(){
pre_init();
scanf("%d%d%s",&n,&w,S+1);
pre_solve();
scanf("%d",&g);
for(int i=1;i<=g;i++)
{
scanf("%s",S);
pii t=Hash(S);
cnt[t]=i;
}
bool mark=0;
for(int i=1;i<=w;i++)
{
if(check(i))
{
mark=1;
break;
}
}
if(!mark) printf("NO\n");
}