1.字符串hash
利用字符串hash可以方便而高效的解决一些匹配问题
很难写错,容易调试
在空间/时间常数允许的情况下尽量使用双关键字哈希(难卡)
2.KMP算法
在线性时间内解决字符串匹配问题,代码短,但容易写错
利用fail数组保存已经匹配获得的信息从而优化匹配的时间复杂度
3.manacher算法
在线性时间内求出以每个位置为中心的最长回文长度
原理是在每两个字符间加入不属于原字符集的字符,统一长度为奇数和偶数的回文串,充分利用已经获得的信息和回文的对称性来进行优化。
4.trie字典树
利用字符串的公共前缀来节约空间和时间,可以轻松地完成字符串排序等工作。编程复杂度较低,可以用于乱搞。
5.Aho-Corasick自动机
在trie的基础上添加了失配指针,可用于多模板串的匹配问题上。插入基本与trie一样,在构造失配指针时需要用到队列保存待配节点。
6.后缀数组
解决字符串问题的有力武器。后缀数组保存字符串的每个后缀的排名, rank 数组保存相应排名的后缀。利用后缀数组可以 O(n) 求出 height 数组,保存两个相邻排名的后缀的最长公共前缀。利用这三个数组我们可以解决很多字符串问题。
7.后缀自动机、后缀树
后缀自动机可以解决(几乎)所有后缀数组能解决的问题,可惜我不会。
8.回文自动机、回文树
统计一个串每个位置上的回文长度,统计每种回文出现的次数和一些其它的东西,可惜我也不会。
下面是部分算法的代码实现
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const int inf=0x3f3f3f3f;
int getint()
{
int f=1,g=0;char c=getchar();
while(c<'0' || c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0' && c<='9')g=(g<<3)+(g<<1)+c-'0',c=getchar();
return f*g;
}
const int maxn=100005;
namespace Suffix_Array{
char s[maxn];
int height[maxn];
int rank[maxn];
int sa[maxn];
int n;
int k;
bool cmp(int a,int b)
{
if(rank[a]!=rank[b])return rank[a]<rank[b];
else
{
int ra=a+k<=n ? rank[a+k]:-1;
int rb=b+k<=n ? rank[b+k]:-1;
return ra<rb;
}
}
int temp[maxn];
void Get_Suffix_Array()
{
for(int i=0;i<=n;i++)
{
sa[i]=i;
rank[i]=i < n ?s[i] : -1;
}
memset(temp,0,sizeof temp);
for(k=1;k<=n;k<<=1)
{
sort(sa,sa+n+1,cmp);
temp[sa[0]]=0;
for(int i=1;i<=n;i++)
{
temp[sa[i]]=temp[sa[i-1]]+cmp(sa[i-1],sa[i]);
}
for(int i=0;i<=n;i++)
{
rank[i]=temp[i];
}
}
}
void Get_Height_Array()
{
for(int i=0;i<=n;i++)
{
rank[sa[i]]=i;
}
int h=0;
height[0]=0;
for(int i=0;i<n;i++)
{
int j=sa[rank[i]-1];
if(h>0)h--;
for(; j+h<n && i+h<n; h++)
{
if(s[j+h]!=s[i+h])break;
}
height[rank[i]-1]=h;
}
}
}
namespace Aho_Corasick_Automaton{
const int maxk=26;
int next[maxn][maxk];
int fail[maxn];
int end[maxn];
int root;
int tot;
int newnode()
{
memset(next[tot],-1,sizeof next[tot]);
end[tot++]=0;
return tot-1;
}
void init()
{
tot=0;
root=newnode();
}
void insert(char c[])//trie
{
int len=strlen(c);
int now=root;
for(int i=0;i<len;i++)
{
if(next[now][c[i]-'a'] == -1)
{
next[now][c[i]-'a']=newnode();
}
now=next[now][c[i]-'a'];
}
end[now]++;
}
void build()//构造失配指针
{
queue<int> q;
fail[root]=root;
for(int i=0;i<maxk;i++)
{
if(next[root][i] == -1)
{
next[root][i]=root;
}
else
{
fail[next[root][i]]=root;
q.push(next[root][i]);
}
}
while(!q.empty())
{
int now=q.front();
q.pop();
for(int i=0;i<maxk;i++)
{
if(next[now][i] == -1)
{
next[now][i]=next[fail[now]][i];
}
else
{
fail[next[now][i]]=next[fail[now]][i];
q.push(next[now][i]);
}
}
}
}
int query(char c[])
{
int len=strlen(c);
int now=root;
int res=0;
for(int i=0;i<len;i++)
{
now=next[now][c[i]-'a'];
int temp=now;
while(temp != root)
{
res+=end[temp];
end[temp]=0;
temp=fail[temp];
}
}
return res;
}
};
namespace Manacher{
int p[maxn<<1];
char s[maxn<<1];
struct segment{
int l,r;
bool operator < (const segment& seg)const
{
return r<seg.r;
}
};
int n;
segment seg[maxn];
char ch[maxn];
int t[maxn];
void calc()
{
scanf("%s",ch);
n=strlen(ch);
s[0]='$';
s[1]='#';
for(int i=0;i<n;i++)
{
s[(i<<1)+2]=ch[i];
s[(i<<1)+3]='#';
}
int len=2*n+1;
int mx=0;
int pos=0;
int cnt=0;
for(int i=1;i<=len;i++)
{
if(mx>i)
{
p[i]=min(p[2*pos-i],mx-i);
}
else p[i]=1;
while(s[i+p[i]]==s[i-p[i]])p[i]++;
if(p[i]+i>mx){
mx=p[i]+i;
pos=i;
}
}
}
}
namespace Hash{
typedef unsigned long long ull;
const int base=13131;
ull b[maxn];
ull val[maxn];
int n;
char a[maxn];
ull Hash(int l,int r)
{
if(l>r)return 0;
return (val[r]-val[l-1]*b[r-l+1]);
}
void solve()
{
scanf("%s",a+1);
b[0]=1;
n=strlen(a+1);
for(int i=1;i<=n;i++)
{
a[i]-='a';
b[i]=b[i-1]*base;
val[i]=val[i-1]*base+a[i];
}
}
}
int main()
{
return 0;
}