牛客15334 Easygoing Single Tune Circulation(后缀自动机+字典树)
https://ac.nowcoder.com/acm/problem/15334
题意:
给你n个字符串S,每个字符串都由小写字母组成且,每个字符仅出现一次。
再给你m个查询串,问你当前查询串是否是S中的某个字符串的旋律(即)的子串
题解:
首先考虑查询串的长度len,len不同的时候处理方式
这种情况的时候如果是yes则是的一个子串
这种情况下T如果是的旋律的子串的话,T的前个字符一定能够通过循环移动得到。
例如:则能够循环移动得到为T
那么我们怎么做这题呢,对于第一种情况就可以直接在sam上做,对每个的建一个广义sam,查时候暴力走就好了。
如果第一种情况不满足,可以则需要考虑第二种情况。
知道第二种情况的结论,我们可以对的最小循环移动串建字典树。注意到有中每个字符一定会不一样,所以。我们只要暴力找到T第一个字符出现的第二个位置就可以直接把串取出来,这是把它的最小循环移动串丢进去字典树找一遍。如果在字典树里面,再按原串构造一遍和T比较一下就能判断对不对了。
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=3e5+5;
const int mod=1e9+7;
#define pb push_back
#define fi first
#define se second
#define all(x) (x).begin(),(x).end()
#define rep(i,a,n) for (int i=a;i<=n;i++)
#define per(i,a,n) for (int i=n;i>=a;i--)
typedef long long ll;
typedef double db;
typedef vector<int> vi;
typedef pair<int,int> pii;
ll qpow(ll a,ll b){ll ans=1;a%=mod;assert(b>=0);for(;b;b>>=1){if(b&1)ans=ans*a%mod;a=a*a%mod;}return ans;}
ll gcd(ll a,ll b){return b>0?gcd(b,a%b):a;}
int n,m,T;
int a[maxn];
string s[maxn],t[maxn];
struct SAM{
int nxt[maxn<<2][26];
int link[maxn<<2];
int step[maxn<<2];
int sz,last,rt;
void init(){
sz=last=rt=1;
memset(nxt[rt],0,sizeof nxt[rt]);
}
void add(int c){
int np=++sz;
int p=last;
last=np;
step[np]=step[p]+1;
memset(nxt[np],0,sizeof nxt[np]);
while(!nxt[p][c]&&p){
nxt[p][c]=np;
p=link[p];
}
if(p==0){
link[np]=rt;
}else{
int q=nxt[p][c];
if(step[q]==step[p]+1){
link[np]=q;
}else{
int nq=++sz;
memcpy(nxt[nq],nxt[q],sizeof nxt[q]);
link[nq]=link[q];
link[np]=link[q]=nq;
step[nq]=step[p]+1;
while(nxt[p][c]==q&&p){
nxt[p][c]=nq;
p=link[p];
}
}
}
}
bool query(string s){
for(int i=0,p=rt,c;i<s.size();i++){
c=s[i]-'a';
if(!nxt[p][c]) return 0;
p=nxt[p][c];
}
return 1;
}
}sam;
struct trie{
int nxt[maxn<<1][26];
int cnt[maxn<<1];
int sz,rt;
int newNode(){
memset(nxt[sz],0,sizeof nxt[sz]);
cnt[sz]=0;
return sz++;
}
void init(){
sz=0;
rt=newNode();
}
void add(string s){
int p=rt;
for(auto cc:s){
int c=cc-'a';
if(!nxt[p][c]){
nxt[p][c]=newNode();
}
p=nxt[p][c];
}
cnt[p]++;
}
bool query(string s){
int p=rt;
for(int i=0,c;i<s.size();i++){
c=s[i]-'a';
if(!nxt[p][c]) return 0;
p=nxt[p][c];
}
return cnt[p]>0;
}
}tr;
string gao(string s){
int c=30,idx=0;
for(int i=0;i<s.size();i++){
if(s[i]-'a'<c){
c=s[i]-'a';
idx=i;
}
}
string t=s.substr(idx);
t+=s.substr(0,idx);
return t;
}
int main(){
cin>>T;
string tt;
int kase=0;
while(T--){
cin>>n;
sam.init();
tr.init();
for(int i=1;i<=n;i++){
cin>>s[i];
tt=gao(s[i]);
tr.add(tt);
}
for(int i=1;i<=n;i++){
sam.last=1;
for(auto c:s[i]){
sam.add(c-'a');
}
for(auto c:s[i]){
sam.add(c-'a');
}
}
cin>>m;
string q,x,y;
printf("Case #%d:\n",++kase);
while(m--){
cin>>q;
if(sam.query(q)){
cout<<"YES\n";
}else{
int pos=q.size();
for(int i=1;i<q.size();i++){
if(q[i]==q[0]){
pos=i;
break;
}
}
x=q.substr(0,pos);
y=gao(x);
// cout<<y<<"\n";
if(tr.query(y)){
y="";
for(int i=0;i<q.size();i++){
y+=x[i%x.size()];
}
if(y==q){
cout<<"YES\n";
}else{
cout<<"NO\n";
}
}else{
cout<<"NO\n";
}
}
}
}
// cin>>n;
// cin>>n>>m;
return 0;
}
/*
2
4
abcd
acd
ad
d
5
cda
ddddddd
dada
cda
acdca
4
abcd
acd
ad
d
6
dabcdabcdd
cda
ddddddd
dada
cda
acdca
*/
Q.E.D.