KMP
nxt:前缀函数,nxt[i]表示子串ss[0,...,i]最长的相等的真前缀与真后缀的长度.
vector<int> kmp(const string &ss)
{
int len = ss.size();
vector<int> nxt(len, 0);
for(int i = 1, j = 0; i < len; i++)
{
while(j > 0 && ss[i] != ss[j])
j = nxt[j - 1];
nxt[i] = (ss[i] == ss[j] ? ++j : j);
}
return nxt;
}
扩展KMP(Z函数)
z[i]的意义,以0开始的后缀与以i开始的后缀的最长公共前缀的长度
vector<int> z_function(const string& ss)
{
int len = (int)ss.size();
vector<int> z(len, 0);
for(int i = 1, l = 0, r = 0; i < len; i++)//切记不可将i设为0
{
if(i <= r)
z[i] = min(r - i + 1, z[i - l]);
while(i + z[i] < len && ss[z[i]] == ss[i + z[i]])
z[i]++;
if(i + z[i] - 1 > r)
{
l = i;
r = i + z[i] - 1;
}
}
//看需求使用
// z[0] = len;
return z;
}
trie树
insert插入字符串
find查询字符串是否出现过
struct Trie{
enum{ alpha = 26, cr = 'a' };
struct node{
array<int,26> ch;
bool is_leaf;
bool dian;
node()
{
is_leaf = false;
dian = false;
fill(ch.begin(), ch.end(), 0);
}
};
vector<node> tr;
int new_node()
{
tr.emplace_back();
return (int)tr.size() - 1;
}
Trie() { new_node(); }
void insert(const string &ss)
{
int p = 0;
for(char c : ss)
{
if(!tr[p].ch[c - cr])
tr[p].ch[c - cr] = new_node();
p = tr[p].ch[c - cr];
}
tr[p].is_leaf = true;
}
bool find(const string &ss)
{
int p = 0;
for(char c : ss)
{
if(!tr[p].ch[c - cr])
return false;
p = tr[p].ch[c - cr];
}
return tr[p].is_leaf;
}
};
前缀自动机
/*
author:wuzx
*/
#include<bits/stdc++.h>
#define ll long long
#define int long long
#define endl "\n"
#define P pair<int,int>
#define f first
#define s second
using namespace std;
const int inf = 0x3f3f3f3f;
int t;
int n,m,k;
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
string ss;
cin >> ss;
n = ss.size();
vector<int> fail(n + 10);
vector<array<int,26>> ch(n + 10);
ch[0][ss[0] - 'a'] = 1;
// fail[1] = 0;
for(int i = 1; i < n; i++)
{
for(int j = 0; j < 26; j++)
{
if(j == ss[i] - 'a')
{
ch[i][j] = i + 1;
fail[i] = ch[fail[i - 1]][j];
}
else
ch[i][j] = ch[fail[i - 1]][j];
}
// cout << fail[i] << " \n"[i == n - 1];
}
int q;
cin >> q;
while(q--)
{
string s1;
cin >> s1;
m = s1.size();
for(int i = n; i < n + m; i++)
{
for(int j = 0; j < 26; j++)
{
if(j == s1[i - n] - 'a')
{
ch[i][j] = i + 1;
fail[i] = ch[fail[i - 1]][j];
}
else
ch[i][j] = ch[fail[i - 1]][j];
}
cout << fail[i] << " \n"[i == n + m - 1];
}
}
return 0;
}
马拉车
mlc[i]表示以i为中心的最长回文串半径的长度
vector<int> manacher(const string &s1)
{
string ss;
for(char c : s1)
ss += '#', ss += c;
ss += '#';
int len = (int)ss.size();
vector<int> mlc(len);
for(int i = 0, l = 0, r = -1; i < len; i++)
{
int kk = (i > r) ? 1 : min(mlc[l + r - i], r - i);
while(0 <= i - kk && i + kk < len && ss[i - kk] == ss[i + kk])
kk++;
mlc[i] = kk--;
if(i + kk > r)
{
l = i - kk;
r = i + kk;
}
}
int ma = 0, idx;
for(int i = 0; i < len; i++)
{
if(mlc[i] > ma)
{
ma = mlc[i];
idx = i;
}
}
for(int i = 0; i < len; i++)
cout << ss[i] << " \n"[i == len - 1];
for(int i = 0; i < len; i++)
cout << mlc[i] << " \n"[i == len - 1];
string s2 = ss.substr(idx - ma + 1, 2 * ma - 1);
string lstr;
for(char c : s2)
if(c != '#')
lstr += c;
cout << lstr << endl;
return mlc;
}
AC自动机:
/*
author:wuzx
*/
#include<bits/stdc++.h>
#define ll long long
#define endl "\n"
#define P pair<int,int>
#define f first
#define s second
using namespace std;
typedef unsigned long long ull;
const int maxn=200010;
const int inf=0x3f3f3f3f;
const int mod=998244353;
int t;
int n,m,k;
struct automaton{
enum {alpha = 26, cr = 'a'};
int id;
using ic = array<int, alpha >;
vector<ic> tr;
vector<int> back,end,cnt,output;
automaton():id(0),tr(1),back(1,0),end(1,-1),cnt(1,0),output(1,-1)
{
fill(tr[id].begin(),tr[id].end(),-1);
}
void add(int v)
{
tr.emplace_back(ic());
fill(tr[id].begin(),tr[id].end(),v);
cnt.emplace_back(0);
back.emplace_back(0);
output.emplace_back(-1);
end.emplace_back(-1);
}
void insert(string &ss,int j)// j: id of string s
{
int p=0,len=ss.length();
for(int i=0;i<len;i++)
{
int x=ss[i]-cr;
if(tr[p][x]==-1)
{
tr[p][x]=++id;
add(-1);
}
p=tr[p][x];
}
end[p]=j;
cnt[p]++;
}
void build()
{
back[0]=++id;
add(0);
queue<int> q;
q.push(0);
while(!q.empty())
{
int p=q.front();
q.pop();
for(int i=0;i<alpha;i++)
{
int pnx=tr[back[p]][i];
int now=tr[p][i];
if(tr[p][i]==-1)
tr[p][i]=pnx;
else
{
back[now]=pnx;
output[now]=end[pnx]==-1?output[pnx]:pnx;
q.push(tr[p][i]);
}
}
}
}
// for each position, finds the longest pattern that ends here
vector<int> find(const string &ss)
{
int len=ss.length();
vector<int> res(len);
int p=0;
for(int i=0;i<len;i++)
{
p=tr[p][ss[i]-cr];
res[i]=end[p];
}
return res;
//返回的数组表示ss串里以第i个位置字符结尾的串的编号
}
// for each position, finds the all that ends here
vector<vector<int>> find_all(const string &ss)
{
int len=ss.length();
vector<vector<int>> res(len);
int p=0;
for(int i=0;i<len;i++)
{
p=tr[p][ss[i]-cr];
res[i].push_back(end[p]);
for(int ind=output[p];ind!=-1;ind=output[ind])
res[i].push_back(end[ind]);
}
return res;
}
//set<int>
int find_cnt(const string &ss,int nn)//输入待查询字符串及插入字符串个数,返回待查询字符串中有多少个插入字符串
{
int len=ss.length();
vector<int> num(nn,0);
int p=0,ans=0;
for(int i=0;i<len;i++)
{
p=tr[p][ss[i]-cr];
if(end[p]!=-1)
{
if(!num[end[p]])
{
num[end[p]]++;
ans+=cnt[p];
}
}
for(int ind=output[p];ind!=-1;ind=output[ind])
{
if(!num[end[ind]])
{
num[end[ind]]++;
ans+=cnt[ind];
}
}
}
return ans;
}
vector<int> find_maxcnt(const string &ss,int nn)//返回出现次数最多的字符串的下标
{
int len=ss.length();
vector<int> num(nn,0);
int p=0,ans=0;
for(int i=0;i<len;i++)
{
p=tr[p][ss[i]-cr];
if(end[p]!=-1)
{
num[end[p]]++;
ans=max(ans,num[end[p]]);
}
for(int ind=output[p];ind!=-1;ind=output[ind])
{
num[end[ind]]++;
ans=max(ans,num[end[ind]]);
}
}
cout<<ans<<endl;
vector<int> idx;
for(int i=0;i<nn;i++)
if(num[i]==ans)
idx.push_back(i);
return idx;
}
};
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>t;
while(t--)
{
int n;
cin>>n;
automaton ac1;
string s1[n],ss;
for(int i=0;i<n;i++)
{
cin>>s1[i];
ac1.insert(s1[i],i);
}
ac1.build();
cin>>ss;
cout<<ac1.find_cnt(ss,n)<<endl;
}
return 0;
}
字符串哈希:
struct string_hash{
const int nn;
const ll Base1 = 29, MOD1 = 1e9 + 7;
const ll Base2 = 131, MOD2 = 1e9 + 9;
vector<ll> ha1, ha2, pow1, pow2;
vector<ll> rha1, rha2;
string_hash(const string &ss,int n1) : nn(n1), ha1(nn + 1), ha2(nn + 1), pow1(nn + 1), pow2(nn + 1), rha1(nn + 1), rha2(nn + 1)
{
pow1[0] = pow2[0] = 1;
for(int i = 1; i <= nn; i++)
{
pow1[i] = pow1[i - 1] * Base1 % MOD1;
pow2[i] = pow2[i - 1] * Base2 % MOD2;
}
for(int i = 1; i <= nn; i++)
{
ha1[i] = (ha1[i - 1] * Base1 + ss[i - 1]) % MOD1;
ha2[i] = (ha2[i - 1] * Base2 + ss[i - 1]) % MOD2;
rha1[i] = (rha1[i - 1] * Base1 + ss[nn - i]) % MOD1;
rha2[i] = (rha2[i - 1] * Base2 + ss[nn - i]) % MOD2;
}
}
pair<ll, ll> get_hash(int l,int r)
{
ll res1 = ((ha1[r] - ha1[l - 1] * pow1[r - l + 1]) % MOD1 + MOD1) % MOD1;
ll res2 = ((ha2[r] - ha2[l - 1] * pow2[r - l + 1]) % MOD2 + MOD2) % MOD2;
return {res1, res2};
}
pair<ll, ll> get_rhash(int l, int r)
{
ll res1 = ((rha1[n - l + 1] - rha1[n - r] * pow1[r - l + 1]) % MOD1 + MOD1) % MOD1;
ll res2 = ((rha2[n - l + 1] - rha2[n - r] * pow2[r - l + 1]) % MOD2 + MOD2) % MOD2;
return {res1, res2};
}
bool is_palindrome(int l, int r)//判断ss[l, r]是否为回文串
{
return get_hash(l, r) == get_rhash(l, r);
}
pair<ll, ll> add(pair<ll, ll> aa,pair<ll, ll> bb)
{
ll res1 = (aa.f + bb.f) % MOD1;
ll res2 = (aa.s + bb.s) % MOD2;
return {res1, res2};
}
pair<ll, ll> mul(pair<ll, ll> &aa, ll kk) //aa *= Base的k次方
{
ll res1 = aa.f * pow1[kk] % MOD1;
ll res2 = aa.s * pow2[kk] % MOD2;
return {res1, res2};
}
};
后缀数组SA
倍增排序
pair<vector<int>,vector<int>> SA(string &ss,int len)
{
ss = '#' + ss;
vector<int> sa(len << 1|1),rk(len << 1|1);
for(int i = 1; i <= len ;i++)
{
sa[i] = i;
rk[i] = ss[i];
}
for(int w = 1; w < len; w <<= 1)
{
sort(sa.begin() + 1, sa.begin() + 1 + len, [&](int x, int y)
{
return rk[x] == rk[y] ? rk[x + w] < rk [y + w] : rk[x] < rk[y];
});
auto oldrk = rk;
for(int p = 0, i = 1; i <= len; i++)
{
if(oldrk[sa[i]] == oldrk[sa[i - 1]] &&
oldrk[sa[i] + w] == oldrk[sa[i - 1] + w])
rk[sa[i]] = p;
else
rk[sa[i]] = ++p;
}
}
for(int i = 1; i <= len; i++)
cout << sa[i] << " ";
cout << endl;
return make_pair(sa,rk);
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
string ss;
cin >> ss;
int len = ss.size();
auto [sa,rk] = SA(ss,len);
return 0;
}
计数排序
void SA(string &ss,int len,vector<int> &sa,vector<int> &rk,vector<int> &height)
{
ss = '#' + ss;
int mm = max(len,300);//串串题先看要不要开ll
sa.assign(len << 1|1,0);
rk.assign(len << 1|1,0);
height.assign(len + 1,0);
vector<int> cnt(mm + 1,0),id(len + 1,0),px(len + 1,0);
for(int i = 1; i <= len; i++)
++cnt[rk[i] = ss[i]];
for(int i = 1; i <= mm; i++)
cnt[i] += cnt[i - 1];
for(int i = len; i >= 1; i--)
sa[cnt[rk[i]]--] = i;
int i,p;
for(int w = 1; ; w <<= 1 , mm = p)
{
for(p = 0,i = len; i > len - w; i--)
id[++p] = i;
for(i = 1; i <= len; i++)
if(sa[i] > w)
id[++p] = sa[i] - w;
cnt.assign(mm+1,0);
for(i = 1; i <= len; i++)
++cnt[px[i] = rk[id[i]]];
for(i = 1; i <= mm; i++)
cnt[i] += cnt[i - 1];
for(i = len; i >= 1; i--)
sa[cnt[px[i]]--] = id[i];
auto oldrk = rk;
auto cmp = [&](int x,int y,int ww)
{
return oldrk[x] == oldrk[y] && oldrk[x + ww] == oldrk[y + ww];
};
for(p = 0, i = 1; i <= len; i++)
rk[sa[i]] = cmp(sa[i],sa[i - 1],w) ? p : ++p;
if(p == len)
{
for(i = 1; i <= len; i++)
sa[rk[i]] = i;
break;
}
}
for(int i = 1; i <= len; i++)
cout << sa[i] << " ";
cout << endl;
for(int i = 1, h = 0; i <= len; i++)
{
if(rk[i] == 0)
continue;
h = h != 0 ? --h : 0;
while(ss[i + h] == ss[sa[rk[i] - 1] + h])
++h;
height[rk[i]] = h;
}
return ;
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
string ss;
cin >> ss;
int len = ss.size();
vector<int> sa,rk,height;
SA(ss,len,sa,rk,height);
return 0;
}
后缀自动机
struct SAM{
enum{alpha = 26, ch = 'a'};
struct state{
int len = 0,link = -1;
array<int,alpha> nxt;
state()
{
len = 0;
link = -1;
fill(nxt.begin(),nxt.end(),0);
}
};
int last = 0;
vector<state> st;
void extend(char c)
{
int cur = (int)st.size();
st.emplace_back();
st[cur].len = st[last].len + 1;
int p = last;
while(p != -1 &&!st[p].nxt[c - ch])
{
st[p].nxt[c - ch] = cur;
p = st[p].link;
}
if(p == -1)
st[cur].link = 0;
else
{
int q = st[p].nxt[c-ch];
if(st[p].len + 1 == st[q].len)
st[cur].link = q;
else
{
int clone = (int)st.size();
st.push_back(st[q]);
st[clone].len = st[p].len + 1;
while(p != -1 && st[p].nxt[c - ch] == q)
{
st[p].nxt[c - ch] = clone;
p = st[p].link;
}
st[q].link = st[cur].link = clone;
}
}
last = cur;
}
SAM() {st.emplace_back();}
SAM(const string &ss): SAM()
{
for(char c:ss)
extend(c);
}
bool find(string &s1)//查询子串
{
int now=0;
for(auto c:s1)
{
if(st[now].nxt[c- ch])
now=st[now].nxt[c - ch];
else
return false;
}
return true;
}
long long cal()//不同子串个数
{
int nn=(int)st.size();
long long ans = 0;
for(int i = 1; i < nn; i++)
ans += st[i].len - st[st[i].link].len;
return ans;
}
};
回文自动机(回文树)
struct PAM{
enum{ alpha = 26, cr = 'a'};
int sz, tot, last;
string ss;
struct node{
int cnt, fail, len;
array<int, alpha> ch;
node()
{
cnt = 0, fail = 0;
fill(ch.begin(), ch.end(), 0);
}
};
vector<node> st;
int new_node(int l)
{
sz++;
st.emplace_back();
st[sz].len = l;
return sz;
}
void init()//初始化要先init,再一个个insert
{
sz = -1;
last = 0;
tot = 0 ;
ss = '$';
new_node(0);
new_node(-1);
st[0].fail = 1;
}
int getfail(int x)
{
while(ss[tot - st[x].len - 1] != ss[tot])
x = st[x].fail;
return x;
}
void insert(char c)
{
ss += c;
tot++;
int now = getfail(last);
if(!st[now].ch[c - cr])
{
int x = new_node(st[now].len + 2);
st[x].fail = st[getfail(st[now].fail)].ch[c - cr];
st[now].ch[c - cr] = x;
}
last = st[now].ch[c - cr];
st[last].cnt++;
}
PAM(const string &s1)
{
init();
for(char c : s1)
insert(c);
}
};