给定 ,给定一个长为 的环形字符串,已知它由 个长为 的互不相同字符串相接而成。
再给定 个长为 的字符串,两两不同,总长不超过 .
问环形字符串是由哪些字符串首尾相接(每个字符串只能出现一次)组成的,输出时可以选定任意的起点,无解输出 。
将环形字符串展开到 长度,因为所有字符串长度相等,所以每个位置只会匹配一个模式串。用AC自动机找到每个位置所匹配的编号。
把位置按模 分组,对于每个模数的 个位置,看看是否有重复的模式串编号,如果没有就是一组解。
这是2300的最后一道题目,感觉2300以下的题有不少kmp的好题,但是一旦涉及自动机就会变得模板了起来,qwq.
/* LittleFall : Hello! */
#include <bits/stdc++.h>
using namespace std; using ll = long long; inline int read();
const int M = 2500016, MOD = 1000000007;
//Trie树(图)/是否为终结点/失配链接/后缀链接,都是节点的属性
int sz = 0;
int ch[M][26], ed[M], fail[M], last[M];
// 向Trie树中尝试插入一个模式串, 返回插入后的节点编号
void insert(const char *s, int id)
{
int u = 0;
for(int i = 0; s[i]; i++)
{
int &v = ch[u][s[i] - 'a'];
if(!v) v=++sz;
u = v;
}
ed[u] = id;
}
void build(const char *s, vector<int> *match, int k)
{
queue<int> q;
for(int v:ch[0]) if(v)
fail[v] = 0, q.push(v);
while(!q.empty())
{
int u = q.front(); q.pop();
for(int i = 0; i < 26; i++)
{
int &v = ch[u][i];
if(v)
{
q.push(v);
fail[v] = ch[fail[u]][i];
last[v] = ed[fail[v]] ? fail[v] : last[fail[v]]; //后缀链接
}
else v = ch[fail[u]][i]; //建立trie图
}
}
int now = 0;
for(int i = 0; s[i]; i++)
{
now = ch[now][s[i] - 'a'];
int id = ed[now] ? now : last[now];
if(ed[id]) match[i%k].push_back(ed[id]); //最多只会匹配一个
}
}
char str[M], pat[M];
vector<int> match[100016]; //每个模数依次匹配的编号
int tag[M];
int main(void)
{
#ifdef _LITTLEFALL_
freopen("in.txt","r",stdin);
#endif
int n = read(), k = read(), m = n*k+k-1;
scanf("%s", str);
for(int i=n*k; i<m; ++i)
str[i] = str[i-n*k];
int g = read();
for(int i=1; i<=g; ++i)
{
scanf("%s", pat);
insert(pat, i);
}
build(str, match, k);
memset(tag, -1, sizeof(tag));
for(int i=0; i<k; ++i) if((int)match[i].size()==n)
{
int suc = 1;
for(auto x:match[i])
{
if(tag[x]==i)
{
suc = 0;
break;
}
tag[x] = i;
}
if(suc)
{
printf("YES\n");
for(auto x:match[i])
printf("%d ",x );
return 0;
}
}
printf("NO\n");
return 0;
}
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}