最长公共回文子串

Longest Common Palindrome Substring ↬ \looparrowright

题目描述

  A palindrome is a symmetrical string, that is, a string read identically from left to right as well as from right to left.
Given two strings which consists of lowercase letters, find the length of the longest common palindrome substring among these strings.

输入描述

  There are several test cases.
  For each test case, there are two single lines contains two string S S S and T T T which only consist of lowercase English letters. ( 1 ≤ ∣ S ∣ , ∣ T ∣ ≤ 1 0 5 1≤|S|,|T|≤10^5 1S,T105)

输出描述

  For each test case, output the length in a single line.

示例1

输入
aba
aaa
ababa
babab
aaaaa
aaaab
输出
1
3
4

Translation

  给两个字符串 S S S T T T ,求最长公共回文子串的长度。

Idea

  读入两个字符串 S 1 S_1 S1 , S 2 S_2 S2
  对于回文的问题,考虑 $\text{Manacher} $ 算法、字符串 Hash \text{Hash} Hash、回文自动机……
  方法一 S 1 S_1 S1 S 2 S_2 S2 统一转化为奇数长度的字符串来处理。
  (ⅰ)第一反应是考虑及其高效的 Manacher \text{Manacher} Manacher 算法。可以用 Manacher \text{Manacher} Manacher 算法求得 S 1 S_1 S1 的所有回文半径。二分公共问文子串的回文半径, check \text{check} check 时,将 S 1 S_1 S1 中回文半径为 m i d mid mid 的回文串的哈希值放入 set \text{set} set ,再扫描 S 2 S_2 S2 中所有长度为 2 × m i d − 1 2\times mid-1 2×mid1的区间哈希值,若某一个哈希值已经在 set \text{set} set 中,说明二分得到的 m i d mid mid 可行。理论上这样的时间复杂度是可以允许的,但是非常玄学地 TLE \text{TLE} TLE 了。
  (ⅱ)这时候想到直接用字符串 Hash \text{Hash} Hash 处理回文。预处理 S 1 S_1 S1 顺序、逆序的 Hash \text{Hash} Hash 值和 S 2 S_2 S2 的顺序 $\text{Hash} $ 值。二分回文半径,每一次 check \text{check} check ,检查 S 1 S_1 S1 中长度为 2 × m i d − 1 2\times mid-1 2×mid1 的区间顺序、逆序 Hash \text{Hash} Hash 是否相等,若相等,将此 Hash \text {Hash} Hash 值放入 set \text{set} set;再扫描 S 2 S_2 S2 中所有长度为 2 × m i d − 1 2\times mid-1 2×mid1的区间哈希值,若某一个哈希值已经在 set \text{set} set 中,说明二分得到的 m i d mid mid 可行。这次 36 m s 36 ms 36ms 就跑完了。一定是我马拉车拉歪来orz。
  方法二 首先将两个字符串拼接起来,中间用特殊符号进行分隔。然后构建这个新的字符串的回文树以及它的 fail树。根据fail树的性质,问题就转化为树上是否存在某个节点,该节点的right集合同时包含两个字符串,而该节点的right集合为其子节点的right集合并。因此对该 fail 树 dfs 递归遍历一遍即可求出每个节点的right集合。时间复杂度为 O ( n ) O(n) O(n)

Code

Hash+二分

#include<set>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define ull unsigned long long
#define N 100002<<1
using namespace std;
const ull base=131;
char s1[N],s2[N];
ull hash1[N],rehash1[N],hash2[N];
ull p[N];
int len1,len2;
void get_pow()
{
	p[0]=1;
	int i;
	for(i=1;i<N;i++) p[i]=p[i-1]*base;
} 
/*--------改造字符串--------*/
void remodel()
{
	len2=strlen(s2+1);
	len1=strlen(s1+1);
	int i;
	for(i=len1<<1;i>0;i-=2)
	{
		s1[i]=s1[i>>1];
		s1[i-1]='{';
	}
	len1=len1<<1|1;
	s1[len1]='{';
	s1[0]='$';
	for(i=len2<<1;i>0;i-=2)
	{
		s2[i]=s2[i>>1];
		s2[i-1]='{';
	}
	len2=len2<<1|1; 
	s2[len2]='{';
	s2[0]='$';
}
/*--------预处理哈希值--------*/
void get_hash()
{
	int i;
	hash1[0]=hash2[0]=0;
	rehash1[len2+1]=0;
	for(i=1;i<=len1;i++) hash1[i]=hash1[i-1]*base+s1[i]-'a'+1;
	for(i=1;i<=len2;i++) hash2[i]=hash2[i-1]*base+s2[i]-'a'+1;
	for(i=len1;i>=1;i--) rehash1[i]=rehash1[i+1]*base+s1[i]-'a'+1;
}
/*---------查询区间哈希值---------*/
ull hash_part(int x,int l,int r)
{
	switch(x)
	{
		case 1:
			return hash1[r]-hash1[l-1]*p[r-l+1];
			break;
		case 2:
			return hash2[r]-hash2[l-1]*p[r-l+1];
			break;
		case 3:
			return rehash1[l]-rehash1[r+1]*p[r-l+1];
			break;
		default:
			break;
	}
}
bool check(int x)
{
	set<ull>ex;
	ull ordered,reordered;
	int l,r;
	int i;
	/*枚举s1的回文中心*/
	for(i=x;i<=len1-x+1;i++)
	{
		l=i-x+1;
		r=i+x-1;
		ordered=hash_part(1,l,r);//顺序
		reordered=hash_part(3,l,r);//逆序
		//两者相等即为回文
		if(ordered==reordered) ex.insert(ordered);
	}
	/*枚举s2中长度为2*mid-1的区间哈希值*/
	for(i=x;i<=len2-x+1;i++)
	{
		l=i-x+1;
		r=i+x-1;
		if(ex.find(hash_part(2,l,r))!=ex.end()) return true;
	}
	return false;
}
int main()
{
	/*预处理base的幂*/
	get_pow();
	while(~scanf("%s%s",s1+1,s2+1))
	{
		remodel();//改造字符串,只处理奇数回文。
		get_hash();//预处理哈希值
		int l=1;
		int r=min(len1,len2)/2+1;
		int ans;
		//二分回文半径
		while(l<=r)
		{
			int mid=l+r>>1;
			if(check(mid))
			{
				ans=mid;
				l=mid+1;
			}
			else r=mid-1;
		}
		printf("%d\n",ans-1/*原字符串回文长度*/);
	}
	return 0;
}

回文自动机

占坑,学完回文自动机就来写

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值