前言
fun fact: 在 c层 听的,下午刚上课比较困,回神来发现讲的听不懂了,光速打开 OiWiki 看了二十分钟,在一听还没我看的快。
Hash 和 KMP
是不是之前写过,直接摆了。this
AC自动机
建立 Trie 树之后,关键是搭建失配指针,大致的还是上 OiWiki 或者别的大佬的博客吧,我写不出来。
板子1
#include<iostream>
#include<queue>
using namespace std;
const int N=1e6+50;
struct Tree{int fail;int vis[30];int end;
}tr[N];
int tot;
void insert(string s){int u=0;for(int i=0;i<(int)s.length();i++){if(tr[u].vis[s[i]-'a']==0){tr[u].vis[s[i]-'a']=++tot;}u=tr[u].vis[s[i]-'a'];}tr[u].end+=1;return ;
}
void build_fail(){queue<int> que;for(int i=0;i<26;i++){if(tr[0].vis[i]!=0){tr[tr[0].vis[i]].fail=0;que.push(tr[0].vis[i]);}}while(!que.empty()){int u=que.front();que.pop();for(int i=0;i<26;i++){if(tr[u].vis[i]!=0){tr[tr[u].vis[i]].fail=tr[tr[u].fail].vis[i];que.push(tr[u].vis[i]);}else{tr[u].vis[i]=tr[tr[u].fail].vis[i];}}}
}
int query(string s){int len=s.length()-1;int u=0,ans=0;for(int i=0;i<=len;i++){u=tr[u].vis[s[i]-'a'];for(int t=u;t&&tr[t].end!=-1;t=tr[t].fail){ans+=tr[t].end;tr[t].end=-1;}}return ans;
}
int main(){int n;cin>>n;for(int i=1;i<=n;i++){string s;cin>>s;insert(s);}tr[0].fail=0;build_fail();string s;cin>>s;cout<<query(s);return 0;
}
板子2
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
const int N=1e6+50;
struct Tree{int fail;int vis[30];int en;
}tr[N];
int tot;
int n;
struct Q{int num,pos;
}q[N];
bool cmp(Q a,Q b){if(a.num==b.num) return a.pos<b.pos;return a.num>b.num;
}
string s[N];
void init(int x){tr[x].fail=tr[x].en=0;memset(tr[x].vis,0,sizeof(tr[x].vis));return ;
}
void insert(string s,int num){int u=0;for(int i=0;i<(int)s.length();i++){if(tr[u].vis[s[i]-'a']==0){tr[u].vis[s[i]-'a']=++tot;init(tot);}u=tr[u].vis[s[i]-'a'];}tr[u].en=num;return ;
}
void build_fail(){queue<int> que;for(int i=0;i<26;i++){if(tr[0].vis[i]!=0){tr[tr[0].vis[i]].fail=0;que.push(tr[0].vis[i]);}}while(!que.empty()){int u=que.front();que.pop();for(int i=0;i<26;i++){if(tr[u].vis[i]!=0){tr[tr[u].vis[i]].fail=tr[tr[u].fail].vis[i];que.push(tr[u].vis[i]);}else{tr[u].vis[i]=tr[tr[u].fail].vis[i];}}}
}
int query(string s){int u=0,res=0;for(int i=0;i<(int)s.length();i++){u=tr[u].vis[s[i]-'a'];for(int t=u;t;t=tr[t].fail){q[tr[t].en].num++;}}return res;
}
int main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);while(cin>>n){if(n==0) break;tot=0;init(0);for(int i=1;i<=n;i++){cin>>s[i];q[i].num=0;q[i].pos=i;insert(s[i],i);}tr[0].fail=0;build_fail();cin>>s[0];query(s[0]);sort(q+1,q+1+n,cmp);cout<<q[1].num<<'\n';cout<<s[q[1].pos]<<'\n';for(int i=2;i<=n;i++){if(q[i].num==q[i-1].num)cout<<s[q[i].pos]<<'\n';elsebreak;}}return 0;
}
manacher马拉车
为了防止讨论奇偶回文串的问题,在原序列每个字符中加入 '#' 然后就是只存在寄回文串了,而且也不影响原回文串,具体操作就是暴力扩展了。
#include<iostream>
#include<cstring>using namespace std;
const int N=2e7+2e6+50;
char s[N];
int cnt;
int pos[N];
int ans=0;
void read(){char c=getchar();s[0]='!';s[++cnt]='#';while(c<'a' || c>'z') c=getchar();while(c>='a' && c<='z'){s[++cnt]=c;s[++cnt]='#';c=getchar();}return ;
}
int main(){read();for(int i=1,r=0,mid=0;i<=cnt;i++){if(i<=r) pos[i]=min(pos[mid*2-i],r-i+1);while(s[i-pos[i]]==s[i+pos[i]]) ++pos[i];if(pos[i]+i>r) r=pos[i]+i-1,mid=i;if(pos[i]>ans) ans=pos[i];}cout<<ans-1;return 0;
}