ACM中常用算法—-字符串
ACM中常用的字符串算法不多,主要有以下几种:
- Hash
- 字典树
- KMP
- AC自动机
- manacher
- 后缀数组
- EX_KMP
- SAM(后缀自动机)
- 回文串自动机
下面来分别介绍一下:
0. Hash
字符串的hash是最简单也最常用的算法,通过某种hash函数将不同的字符串分别对应到不同的数字.进而配合其他数据结构或STL可以做到判重,统计,查询等操作.
- #### 字符串的hash函数:
一个很简单的hash函数代码如下:
ull xp[maxn],hash[maxn];
void init()
{
xp[0]=1;
for(int i=1;i<maxn;i++)
xp[i]=xp[i-1]*175;
}
ull get_hash(int i,int L)
{
return hash[i]-hash[i+L]*xp[L];
}
scanf("%s",str);
int n=strlen(str);
hash[n]=0;
for(int i=n-1;i>=0;i--)
{
hash[i]=hash[i+1]*175+(str[i]-'a'+1);
}
其中175是顺便选择的基数,对一个串通过init的预处理后,就用get_hash(i,L)可以得到从位置i开始的,长度为L的子串的hash值.
其他的一些hash函数介绍 字符串hash函数
hash函数可能会遇到的问题
一般情况下,这个简单的hash函数已经足够好了.但使用hash函数解题的时候还是有问题要注意:
hash函数的结果并不一定准确,hash的值可能会有冲突导致结果错误(但不常遇到可以换hash数即可).
对于一般的字符串,这个hash函数准确性很高. 但是有的题目会刻意构造可以使hash函数失效的字符串,无论换什么样的hash数都过不了,这时就需要对hash函数进行修改,不能使用自然溢出的方式储存hash值,可以选取两个大质数,对用一个字符串记录它的hash值和这两个数的mod.用这种方法可以过掉几乎全部卡hash函数的题
例题
- HDOJ 4821 String
- HDOJ 4080 Stammering Aliens
- HDOJ 4622 Reincarnation
- CSU1647: SimplePalindromicTree
1. 字典树
字典树是储存着不同字符串的数据结构,是一个n叉树(n为字符集的大小),对于一棵储存26个字母的字典树来说,它的的每一个节点储存着26个指针可以分别代表这个节点的后面加上’a’~’z’后可以指向那个节点.
插入的时候从根节点开始,沿着对应的边走(如果某个指针后面指向的节点为空.可以新建一个节点),走到字符串结束的时候在当前停留的节点标记一下(是否出现过,出现了几次等).
查询的时候也是一样从根节点走,如果走到某个节点无路可走了,说明查不到.当一路走到字符串结束时,检查当前停留的节点是否被标记过.
一份代码参考:
/*字典树*/
const int CHAR=26,MAXN=100000;
struct Trie
{
int tot,root,child[MAXN][CHAR];
bool flag[MAXN];
Trie()
{
memset(child[1],0,sizeof(child[1]));
flag[1]=true;
root=tot=1;
}
void Insert(const char *str)
{
int *cur=&root;
for(const char*p=str;*p;p++)
{
cur=&child[*cur][*p-'a'];
if(*cur==0)
{
*cur=++tot;
memset(child[tot],0,sizeof(child[tot]));
flag[tot]=false;
}
}
flag[*cur]=true;
}
bool Query(const char *str)
{
int *cur=&root;
for(const char *p=str;*p&&*cur;p++)
cur=&child[*cur][*p-'a'];
return (*cur)&&flag[*cur];
}
}tree;
例题
- POJ 3630 Phone List
- HDOJ 4622 Reincarnation
- HDOJ 1251 统计难题
2. KMP
kmp是一种字符串匹配的算法,普通的字符串匹配需要时间O(n*m) n:字符串长度 m:模版串长度,kmp算法通过对模版串进行预处理来找到每个位置的后缀和第一个字母的前缀的最大公共长度,可以让复制度降低到O(n+m)
关于KMP算法白书有很详细的介绍,网上也有很多.
一种实现:
char t[1000],p[1000];
int f[1000];
void getfail(char* p,int* f)
{
int m=strlen(p);
f[0]=f[1]=0;
for(int i=1;i<m;i++)
{
int j=f[i];
w