《算法笔记》读书记录DAY_61

CHAPTER_12  提高篇(6)——字符串专题

12.1 字符串hash进阶

(续上节)

我们现在重新用字符串hash+二分的方法来解决11.5节中的最长回文子串的问题。

题目:

给出一个字符串S,字母区分大小写,求S的最大回文子串的长度

输入样例:

PATZJUJZTACCBCC

输出样例:

9       

//最长回文子串为ATZJUJZTA,长度为9

思路:

对于一个给定的字符串str,可以先求出其字符串hash数组H1,然后再将str反转,求出反转字符串str的hash数组H2。接着分回文串的奇偶情况进行讨论。

(1)回文串的长度是奇数:枚举回文中心点 i ,二分子串的半径k,找到最大的使子串[i-k,i+k]是回文串的k。其中判断子串[i-k,i+k]是回文串等价于判断str的两个子串[i-k,i]和[i,i+k]是否是相反的串。而这等价于判断str的[i-k,i]子串与反转字符串rstr的[len-1-(i+k),len-1-i]子串是否相同( [a,b]在反转字符串中的位置为[len-1-(i+k),len-1-i] ),因此只需要判断H1[i-k...i]与H2[len-1-(i+k)...len-1-i]是否相等即可。

(2)回文串的长度是偶数:枚举回文空隙点,令 i 表示空隙左边第一个元素的下标,二分子串的板甲k,找到最大的使子串[i-k+1,i+k]是回文串的k。其中判断子串[i-k+1,i+k]是回文串等价于判断str的两个子串[i-k+1,i]和[i+1,i+k]是否是相反的串。而这等价于判断str的[i-k+1,i]子串与反转字符串rstr的[len-1-(i+k),len-1-(i+1)]子串是否相同,因此只需要判断H1[i-k+1...i]与H2[len-1-(i+k)...len-1-(i+1)]是否相等即可。

参考代码:

#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;
typedef long long LL;
const LL mod=1000000007;
const LL p=10000019;
const int maxn=10001;
LL powP[maxn];                    //powP[i]存放p^i%mod
LL H1[maxn]={0},H2[maxn]={0};     //存放str1,str2的hash值 

//初始化powP函数
void init(int len) {
	powP[0]=1;
	for(int i=1;i<=len;i++) {
		powP[i]=(powP[i-1]*p)%mod;
	}
}

//计算字符串str的hash值 
void calH(LL H[],string &str) {
	H[0]=str[0]-'a';                    //H[0]单独处理
	for(int i=1;i<str.size();i++) {
		H[i]=(H[i-1]*p+str[i]-'a')%mod;
	} 
}

//计算单个H[i...j]
int calSingleSubH(LL H[],int i,int j) {
	if(i==0)
		return H[j];
	else {
		return ((H[j]-H[i-1]*powP[j-i+1])%mod+mod)%mod;
	}
}

//对称点为i,字符串长len,在[l,r]里二分回文半径
//寻找最后一个满足条件hashL==hashR的回文半径
//等价于寻找一个满足条件hashL!=hashR的回文半径,然后减一 
//isEven当求奇回文时为0,偶回文时为1 
int binarySearch(int l,int r,int len,int i,int isEven) {
	while(l<r) {
		int mid=(l+r)/2;
		//左半子串hash值H1[H1L...H1R],右半子串hash值H2[H2L...H2R]
		int H1L=i-mid+isEven,H1R=i;
		int H2L=len-1-(i+mid),H2R=len-1-(i+isEven);
		int hashL=calSingleSubH(H1,H1L,H1R);
		int hashR=calSingleSubH(H2,H2L,H2R);
		if(hashL!=hashR)
			r=mid;                //hash值不等,说明回文半径<=mid 
		else
			l=mid+1;              //hash值相等,说明回文半径>mid 
	}
	return l-1;                   //返回最大回文半径 
} 

int main() {
	string str;
	cin>>str;
	init(str.size());
	calH(H1,str);                         //计算str的hash值 
	reverse(str.begin(),str.end());       //反转字符串
	calH(H2,str);                         //计算rstr的hash值
	int ans=0;
	//偶回文
	for(int i=0;i<str.length();i++) {
		//二分上界为分界点i的左右长度的较小值加一 
		int maxLen=min(i+1,(int)str.size()-1-i)+1;
		int k=binarySearch(0,maxLen,str.size(),i,1);
		ans=max(ans,k*2);
	} 
	for(int i=0;i<str.length();i++) {
		//二分上界为分界点i的左右长度的较小值加一 
		int maxLen=min(i,(int)str.size()-1-i)+1;
		int k=binarySearch(0,maxLen,str.size(),i,0);
		ans=max(ans,k*2+1);		
	}
	cout<<ans<<endl; 
	return 0;
}

猜你喜欢

转载自blog.csdn.net/jgsecurity/article/details/121395302