字符串专题复习
字符串已经忘光了,只好花了一天时间来复习
KMP
code:
#include<bits/stdc++.h>
using namespace std;
string s,t;
int n,m;
int nxt[1000006];
void getnxt(){
nxt[0]=-1;int i=0,j=-1;
while(i<m){
if(j==-1||t[i]==t[j])
i++,j++,nxt[i]=j;
else j=nxt[j];
}
}
void match(){
int j=0;
for(int i=0;i<n;i++){
while(s[i]!=t[j]&&j!=-1)j=nxt[j];
j++;if(j==m)cout<<i-m+2<<'\n';
}
}
signed main(){
cin>>s>>t;n=s.length(),m=t.length();
getnxt();
match();
for(int i=1;i<=m;i++)
cout<<nxt[i]<<" ";
return 0;
}
AC自动机
trie树上的KMP
将多个模式串上trie树,一个文本串来匹配模式串。
fail 类似于 KMP 的 nxt
概括 AC自动机 求 fail 的过程:
1.对整个字典树进行宽度优先遍历。
2.若当前搜索到点 \(x\),那么对于 \(x\) 的第 \(i\) 个儿子(也就是代表字符 \(i\) 的儿子),一直往 \(x\) 的 fail 跳,直到跳到某个点也有 \(i\) 这个儿子,\(x\) 的第 \(i\) 个儿子的 fail 就指向这个点的儿子 \(i\)。
形式上,AC 自动机基于由若干模式串构成的 Trie 树,并在此之上增加了一些 fail 边;本质上,AC 自动机是一个关于若干模式串的 DFA(确定有限状态自动机),接受且仅接受以某一个模式串作为后缀的字符串。
并且,与一般自动机不同的,AC 自动机还有 关于某个模式串的接受状态,也就是与某个模式串匹配(以某个模式串为后缀)的那些状态,即某个模式串在 Trie 树上的终止节点在 fail 树上的整个子树。
关于 last 其实是个路径压缩,保证跳的时候一定跳到有模式串的地方,可以把 暴力跳fail 优化到 \(O(模式串总长)\)。
code:
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
char s[N*10];
int c[N][26],tot,b[N],fail[N],last[N],n;
int ans[N];
int flag[N];
void insert(int T){
cin>>s;
int u=0,len=strlen(s);
for(int i=0;i<len;i++){
int t=s[i]-'a';
if(!c[u][t]) c[u][t]=++tot;
u=c[u][t];
}
if(b[u])flag[T]=b[u];
else b[u]=T;
}
void getfail(){
queue<int> q;
for(int i=0;i<26;i++) if(c[0][i]) q.push(c[0][i]);
while(!q.empty()){
int u=q.front();q.pop();
for(int i=0;i<26;i++){
int v=c[u][i];
if(!v){c[u][i]=c[fail[u]][i];continue;}
q.push(v);fail[v]=c[fail[u]][i];
last[v]=b[fail[v]]?fail[v]:last[fail[v]];
}
}
}
void find(){
cin>>s;
int u=0,len=strlen(s);
for(int i=0;i<len;i++){
int t=s[i]-'a',temp=0;
u=c[u][t];
if(b[u])temp=u;
else if(last[u])temp=last[u];
while(temp){
ans[b[temp]]++;
temp=last[temp];
}
}
}
void solve(){
cin>>n;
for(int i=1;i<=n;i++)insert(i);
getfail();find();
for(int i=1;i<=n;i++)cout<<(flag[i]?ans[flag[i]]:ans[i])<<'\n';
}
int main(){
solve();
return 0;
}
manacher
注意其中对于 \(hw_i\) 初始化部分,hw[i]=min(hw[(mid<<1)-i],hw[mid]+mid-i);
其中 hw[mid]+mid-i
等价于 maxright-i
这实际上是 \(i\) 在 \(mid\) 左边相对应的 \(j\) 所受的限制,因为 \(hw_j\) 可能会超出 \(mid-hw[mid]\) 的范围,所以需要限定最大值。画图之后翻折过去就能理解。
code:
#include<bits/stdc++.h>
using namespace std;
const int N=11000005;
string t="@#";int n;
inline void gett(){
char c=getchar();
while(c>'z'||c<'a')c=getchar();
while(c>='a'&&c<='z')t+=c,t+="#",c=getchar();
n=t.length();
}
int hw[2*N],mr,mid,ans;
inline void match(){
for(int i=1;i<n;i++){
hw[i]=(mr>i)?min(hw[(mid<<1)-i],mr-i):1;
while(t[i+hw[i]]==t[i-hw[i]])hw[i]++;
if(i+hw[i]>mr)mr=i+hw[i],mid=i;
ans=max(ans,hw[i]);
}
}
int main(){
gett();
match();
cout<<ans-1;
return 0;
}
update on 2022.1.21
Z算法
Z-algorithm & kmp & 循环节
后缀数组