【week2day3笔记】字符串初步

KMP
KMP用于快速匹配字符串,查出一个串在另一个串中出现的位置及次数。
KMP的核心是失配数组的构建以及应用。
失配数组:习惯命名为next[],next[i]中存储信息含义是: 字符串前i位构成的字串中,若存在该串的某一前缀与后缀完全相同,则存储满足该条件的最长前缀/后缀长度(next[1]记为0)。如串abab,next[1]=0,next[2]=0,next[3]=1,next[4]=2;,在匹配中对模板串(即需要在另一串中查询其位置/次数的串)构建next数组
可以注意到,失配数组中存储的信息,也可用于表示上一个与此处相同的字串的末尾位置(如abab中,末尾ab与前端ab相同,next[4]中值为2,标记着前一个ab的末尾位置)
在匹配过程中,若出现失配(当前匹配到的位置上两串字符不同),利用失配数组可以快速前移模板串,使得模板串该部分的前缀,与之前已匹配过的,内容完全一样的后缀位置重合,并继续匹配,大幅度减短失配造成的调整时间
先看看如何运用next数组:

for(int i=0,j=0;t[i];i++)                                //w为模板串
{
    while(j&&w[j]!=t[i])j=next[j];
    if(t[i]==w[j])j++;
    if(j==len)ans++;
}

而构建next数组的过程,思路上可看作自己与自己匹配

void getnext()
{
    int l=strlen(w);
    for(int i=1;i<l;i++)
	{
        int j=next[i];
        while(j&&w[i]!=w[j])j=next[j];
        next[i + 1] = w[i] == w[j] ? j + 1 : 0;
    }
}

除用于匹配外,next数组还存在一些其他性质,如最小循环节的长度等

trie树
中文名字典树,是一种用于存储多个字符串的数据结构
trie树示意图:(图片来自百度百科)
在这里插入图片描述
trie树存储形式:
int ch[][26],其中存储的值为大于等于0的整数,0表示此处不是单词节点,正数表示此处是一个单词(已存储的字符串)

trie树的插入:

void insert(int x)
{
    int n = strlen(tmp),p = 0;
    for(int i = 0;i < n;i ++) {
        int c = tmp[i] - 'a' + 1;
        if(!ch[p][c]) ch[p][c] = ++ Tcnt;
        p = ch[p][c];
    }
    val[p] = x;
}

manacher(马拉车)算法
马拉车算法用于计算字符串中每一个点,以其为中心,最长的回文串长度
马拉车算法分两步:
①两两字符插入特殊无关字符,将原串变为长度固定为奇数的串,便于处理
②暴力运算每个点扩展出的回文串长度,但当当前处理的点已存在于现前发现的回文串中时,可利用对称性质减少判定次数
(扩展意义似乎不大,直接上代码)

void prepare() {
    n = strlen(s1 + 1);
    s2[++len] = '@';
    s2[++len] = '#';
    for (int i = 1; i <= n; i++) {
        s2[++len] = s1[i];
        s2[++len] = '#';
    }
    s2[++len] = '\0';
}

void manacher() {
    int right = 0, pos = -1;
    for (int i = 1; i <= len; i++) {
        int x;
        if (right < i) x = 1;
        else x = std::min(r[2 * pos - i], right - i);
        while (s2[i - x] == s2[i + x]) x++;
        if (x + i > right) {
            right = x + i;
            pos = i;
        }
        r[i] = x;
    }
}

例题
POJ - 2752(kmp)

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
char s[400010];
int next[400010];
void getnext()
{
    int l=strlen(s);
    for(int i=1;i<l;i++)
	{
        int j=next[i];
        while(j&&s[i]!=s[j])j=next[j];
        next[i+1]=s[i]==s[j]?j+1:0;
    }
}

int main()
{
	int ans[400010],cnt;
	while(scanf("%s",s)!=EOF)
	{
		cnt=0;
		int len=strlen(s);
		next[0]=next[1]=0;
		getnext();
		while(len)
		{
			ans[cnt++]=len;
			len=next[len];
		}
		for(int i=cnt-1;i>=0;i--)
			printf("%d ",ans[i]);
		printf("\n");
	}
	return 0;
}

POJ 3974(马拉车)

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
char s[1000010];
char t[2000050];
int r[2000050];
int len;
void prepare()
{
	t[0]='@';
	t[1]='#';
	len=2;
	for(int i=0;s[i];i++)
	{
		t[len++]=s[i];
		t[len++]='#';
	}
	t[len++]=0;
}
int main()
{
	int cnt=0,right,pos,ans;
	scanf("%s",s);
	while(!(s[0]=='E'&&s[1]=='N'&&s[2]=='D'))
	{
		ans=0;
		right=0;pos=-1;
		printf("Case %d: ",++cnt);
		prepare();
		for(int i=1;i<=len;i++)
		{
			int x;
			if(right<i)x=1;
			else x=min(r[2*pos-i],right-i+1);
			while(t[i-x]==t[i+x])x++;
			if(x+i-1>right)
			{
				right=x+i-1;
				pos=i;
			}
			r[i]=x;
			int res;
			if(t[i]=='#')res=(x/2)*2;
			else res=((x-1)/2)*2+1;
			if(res>ans)ans=res;
		}
		printf("%d\n",ans);
		/*for(int i=1;i<len;i++)printf("%c",t[i]);
		cout<<endl;
		for(int i=1;i<len;i++)printf("%d ",r[i]);*/
		scanf("%s",s);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值