马拉车算法和字符串哈希

马拉车算法是解决最长回文子串的有力武器
例如 string s=“abadccda”
我们要想求出最长回文子串一般都是暴力,枚举子串的左右端点,一般枚举是o(n2)的复杂度,判断又是又要o(n),也可以用字符串哈希来判断,不过铁定超时.
但马拉车可以坐到o(n)的复杂度,他和kmp的思想很类似就保存前面的信息来利用.
设a[i]表示以i为回文中心的最长回文半径
例如aba(以1为第一个下标) a[2=2;
我们如何求出每一个位置的a[i]呢?
string s=“abadccda”
因为回文子串长度分奇偶,如何处理呢(字母之间插入不相关的字符)
t=#a#b#a#d#c#c#d#a#
这样就都变成了奇回文子串
最终形态
t=!#a#b#a#d#c#c#d#a#$
这样是为了暴力扩展而添加的 (1)
设mid为对称中心,mid之前的a[i]都已经求出来,mr为mid+a[mid]-1,j为i的对称点求a[i]在这里插入图片描述

如果以j为回文子串的子串在以mid为回文子串的子串中,那么以i为回文中心的子串等于以j为回文子串的子串,所以a[i]=a[j]
在这里插入图片描述

如果以j为回文子串的子串的左边界大于以mid为回文子串的子串的左边界,那么只能保证以j为回文中心以a[j]-1为回文半径的子串是等于以i为回文中心的子串,因为他们关与mid对称,
之后再对i为回文中心的子串进行暴力扩展,因为这个子串的回文半径还课能更大,(1)处就是为防止暴力扩展越界,
之后更新更大的mr,和mid
最大回文子串长度为最大回文子串半径减一
模板题

#include"bits/stdc++.h"
using namespace std;
char s[22000005],t[32000005];
int a[22000005];
int cnt=0;
int main(){
	scanf("%s",s+1);
	int n=strlen(s+1);
	t[++cnt]='!';
	t[++cnt]='#';
	for(int i=1;i<=n;i++){
		t[++cnt]=s[i];
		t[++cnt]='#';
	}
	t[++cnt]='$';
	int mid=0,mr=0,ma=0;
	for(int i=2;i<=cnt-1;i++){
		if(i<=mr){
			a[i]=min(a[2*mid-i],mr-i+1);
		}
		else a[i]=1;
		while(t[i-a[i]]==t[i+a[i]])++a[i];
		if(i+a[i]>mr){
			mid=i;
			mr=i+a[i]-1;
		}
		ma=max(ma,a[i]);
	}
	cout<<ma-1;
	return 0;
} 

字符串哈希比较简单
o(n)的预处理,o(1)的比较字符串
思想就是把一个字符串映射到一个数上,两个字符串比较就为两个数比较
例如
s=“abcde”
我们把s当做一个131进制的数(为什么是131因为会很小概率出现哈希冲突)
s='e’pow(131,0)+'dpow(131,1)+‘c’*pow(131,2)+‘b’*pow(131,3)+‘a’*pow(131,4);
此时s就被映射为一个数,当字符串位数的比较多时,数就很大,我们要对他求余,不过当我们把他设为unsigned long long 时溢出就会自动对pow(2,64)求余,所以我们设为unsigned long long 就行,
hash1[i]表示已i为结束字符串哈希值,base[i]表示pow(131,i);
求[L,R]的字符串哈希值为hash1[R]-hash1[L-1]*base[R-L+1];
有点像前缀和了
模板题

#include"iostream"
#include"string.h"
using namespace std;
typedef unsigned long long ull;
const int base=131;
ull hashh[1000005],p[1000005];
char s[1000005];
int m;
int main(){
	scanf("%s",s+1);
	p[0]=1;
	hashh[0]=0;
	int len=strlen(s+1);
	for(int i=1;i<=len;i++){
		hashh[i]=hashh[i-1]*base+s[i]-'a'+1;
		p[i]=p[i-1]*base;
	}
	cin>>m;
	for(int i=1;i<=m;i++){
		int l1,r1,l2,r2;
		cin>>l1>>r1>>l2>>r2;
		ull a=hashh[r1]-hashh[l1-1]*p[r1-l1+1];
		ull b=hashh[r2]-hashh[l2-1]*p[r2-l2+1];
		
		if(a==b){
			cout<<"Yes"<<endl;
		}
		else{
			cout<<"No"<<endl;
		}
	}
	return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值