基础字符串(⊙x⊙;)

字符串Hash

每个字符对于一个ASCII码,即可用一个数字表示
将字符串转换为一个p进制数(常取p=131)
hash代码如下:

const int p=131;
const int mod=99999991;
int Hash(string s){
	int hash=0;
	for(int i=0;i<strlen(s);i++)
		hash=(hash*p+s[i])%mod;
	return hash;
}

但单Hash仍比较容易发生冲突,所以可维护字符串长度来降低冲突。

STL_hash

unordered_map
unordered_set

例题

CF 955D Scissors
ICPC 2019 Shanghai Online Contest G
CF 985F Isomorphic Strings

二维Hash

横行求一遍hash值,数列再求一遍

    for(int i=1;i<=n;i++){
        scanf("%s",s+1);
        for(int j=1;j<=m;j++)
            v[i][j]=s[j]+v[i][j-1]*base1+v[i-1][j]*base2-v[i-1][j-1]*base1*base2;
    }
    ull c=1,d=1;
    for(int i=1;i<=a;i++) d*=base2;
    for(int i=1;i<=b;i++) c*=base1;
    for(int i=a;i<=n;i++)
        for(int j=b;j<=m;j++)
            mp.insert(make_pair(v[i][j]-v[i][j-b]*c-v[i-a][j]*d+v[i-a][j-b]*c*d,1));

Trie树

又称字典树,单词查找树。
性质

  • 除根节点外,每个节点都包含一个字符
  • 从根到某一节点所经过的路径上的字符相连,即为该节点对应的字符串
  • 每个节点的子节点互不相同

代码如下:
建树

void insert(char *str){
   int len=strlen(str),root=0;
   for(int i=0;i<len;i++){
       int id=str[i]-'0';
       if(!tree[root][id]) tree[root][id]=++tot;
       root=tree[root][id];
   }
   flagg[root]=true;
}

查询

bool find(char *str){
    int len=strlen(str),root=0;
    for(int i=0;i<len;i++){
        int id=str[i]-'0';
        if(!tree[root][id]) return false;
        root=tree[root][id];
    }
    return true;
}

例题

LA3942 Remember the Word
Leetcoder421 Maximum XOR of Two Numbers in an Array
NEERC11D Dictionary Size


AC自动机

一种字符串匹配算法。适用于多模式串。
通过在trie树上维护失配边fail来实现。

建树

void insert(string s){
    int root=0;
    for(int i=0;i<s.size();i++){
        int next=s[i]-'a';
        if(!trie[root][next])
            trie[root][next]=++cnt;
        root=trie[root][next];
    }
    cntword[root]++; 
}
void getfail(){
    queue<int>q;
    for(int i=0;i<26;i++){
        if(trie[0][i]){
            fail[trie[0][i]]=0;
            q.push(trie[0][i]);
        }
    }
    while(!q.empty()){
        int now=q.front();
        q.pop();
        for(int i=0;i<26;i++){
            if(trie[now][i]){
                fail[trie[now][i]]=trie[fail[now]][i];
                q.push(trie[now][i]);
            }
            else trie[now][i]=trie[fail[now]][i];
        }
    }
}

查询

int find(string s){
    int now=0,ans=0;
    for(int i=0;i<s.size();i++){
        now=trie[now][s[i]-'a'];
        for(int j=now;j&&cntword[j]!=-1;j=fail[j]){
            ans+=cntword[j];
            cntword[j]=-1;
        }
    }
    return ans;
}

性质
每个节点不仅是一个单词的结尾,也包含了所有以它为结尾作为前缀的串

例题

JSOI2007文本生成器
2018 ACM-ICPC Beijing Onsite H Approximate Matching
HEOI2012旅行问题


KMP

一种字符串匹配算法。相当于退化的AC自动机,主要依赖于一个next数组
next数组表示:当前字符之前的字符串中,有多大长度的相同前缀后缀。
如何预处理出next数组?

next[0]=-1;
int j=-1;
for(int i=1;i<b.size();i++){
	while((j>=0)&&(b[j+1]!=b[i])) j=next[j];
	if(b[j+1]==b[i])j++;
	next[i]=j;
}

匹配:

j=-1;
for(int i=0;i<a.size();i++){
	while((j>-1)&&(b[j+1]!=a[i]))j=next[j];
	if(b[j+1]==a[i])j++;
	if(j==b.size()-1){
		ans++;
		j=next[j];
	}
}

例题

HDU3336 Count the string
ZOJ1905 Power Strings
POI2006 OKR-PERIODS OF WORDS


扩展KMP

和kmp比较类似。
next数组的含义稍有不同,
next[i]表示t[i]…t[m-1]于t的最长相同前缀长度,即相当于t自己和自己匹配
当i+next[i-a]与p的大小关系不同时,答案extend[i]的值也不同。

  1. i+next[i-a]<p时,extend[i]=next[i-a]
  2. i+next[i-a]=p时,直接从s[p]与t[p-i]开始匹配
  3. i+next[i-a]>p时,extend[i]=p-i;
void pre_EKMP(char x[],int m,int nxt[])
{
	nxt[0]=m;
	int j=0;
	while(j+1<m && x[j]==x[j+1]) j++;
	nxt[1]=j;
	int k=1;
	for(int i=2;i<m;i++)
	{
		int p=nxt[k]+k-1;
		int L=nxt[i-k];
		if(i+L<p+1)nxt[i]=L;
		else
		{
			j=max(0,p-i+1);
			while(i+j<m && x[i+j]==x[j])j++;
			nxt[i]=j;
			k=i;
		}
	}
}

void EKMP(char x[],int m,char y[],int n,int nxt[],int extend[])
{
	pre_EKMP(x,m,nxt);
	int j=0;
	while(j<n && j<m && x[j]==y[j]) j++;
	extend[0]=j;
	int k=0;
	for(int i=1;i<n;i++)
	{
		int p=extend[k]+k-1;
		int L=nxt[i-k];
		if(i+L<p+1)
			extend[i]=L;
		else
		{
			j=max(0,p-i+1);
			while(i+j<n && j<m && y[i+j]==x[j]) j++;
			extend[i]=j;
			k=i;
		}
	}
}

例题

HDU2594 SIMPSONS’ HIDDEN TALENTS
2019 Multi-University,HDU Day5 D


Manacher

回文串有奇数偶数之分,若在每个字符之间插入一个#,在开头和结尾分别插入两个不常用的字符表示边界(如%、$、@…),即可将所有回文串变成奇数。
设p[i]表示以i为中心的最长回文的半径,p[i]-1即为回文串的长度。
所以怎么预处理出p数组呢?

  1. i>=maxright 中心扩散法
  2. i<maxright时
    .1. p[mirror]<maxright时 p[i]=p[mirror]
    .2. p[mirror]=maxright-i时 p[i]>=maxright-i,继续扩展
    .3. p[mirror]>maxright-i时 p[i]=maxright-i
void init(){
    len=strlen(s),cnt=1;
    ss[0]='!';ss[cnt]='#';
    for(int i=0;i<len;i++)
		ss[++cnt]=s[i],ss[++cnt]='#';
}
void manacher(){
    int pos=0,mx=0;
    for(int i=1;i<=cnt;i++){
		if(i<mx) p[i]=min(p[pos*2-i],mx-i);
		else p[i]=1;
		while(ss[i+p[i]]==ss[i-p[i]]) p[i]++;
		if(mx<i+p[i]) mx=i+p[i],pos=i;
		ans=max(ans,p[i]-1);
    }
}

例题

2018 ICPC Nanjing Regional Contest M
NOWCODER14894 最长回文
POI2007 Axes of Symmetry

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值