ZOJ 4110 Strings in the Pocket 题解

传送门

题目描述:

给你两个字符串 s 1 , s 2 s1,s2 s1,s2,你可以选择一个 s 1 s1 s1的子串对它进行反转,使得 s 1 = s 2 s1=s2 s1=s2,问有几种选法。

解题思路:

预备知识:马拉车算法,不会的话建议百度看看别人的博客。
1、首先如果 s 1 ! = s 2 s1!=s2 s1!=s2,那么第一个不等的到最后个不等的一点要反转:
1)判断把他们反正是否一样,如果不一样,那么答案就是 0 0 0
2)如果是一样的,这样一定是一个解,那么我们再往两边扩散看看,如果两边一样那又是一个解,不断重复,知道两边不一样。
2、如果两个字符串完全一样,那么就是求它的回文子串,对于一个长度为 n n n的回文子串,它的贡献是 ( n + 1 ) / 2 (n+1)/2 (n+1)/2,也就是回文半径,这个也比较好理解和上面一样就是以中电向外扩散。那么我们用马拉车算法,计算出每个回文子串的长度就可以了。

代码:

#include <bits/stdc++.h>
using namespace std;
const int maxn=4e7+10;
char s1[maxn],s2[maxn];
int p[maxn];
int main(){
	int t;
	cin>>t;
	while(t--)
	{
		scanf("%s%s",s1,s2);
		int len = strlen(s1);
		int l = -1;
		for(int i=0;i<len;i++)
			if(s1[i] != s2[i])
			{
				l = i;
				break;
			}
		int r = -1;
		for(int i=len-1;i>=0;i--)
			if(s1[i] != s2[i])
			{
				r = i;
				break;
			}
		//l代表左边开始第一个不相等的字符的位置
		//r代表右边开始第一个不相等的字符的位置 
		if(l != -1)
		{
			int f = 1;
			for(int i=0;i<(r-l+1);i++)
				if(s1[i+l] != s2[r-i])
					f=0;	
			if(f == 0)
				printf("0\n");
			else
			{
				int ans = 1;
				while(l > 0 && r + 1 < len && s1[l-1] == s1[r+1])
				{
					l--;
					r++;
					ans++;
				}
				printf("%d\n",ans);
			}
		}
		else
		{
			//马拉车 
			int n = 1;
			s2[0] = '#';
			for(int i=0;i<len;i++)
			{
				s2[n++] = '#';
				s2[n++] = s1[i]; 
			}
			s2[n++] = '#';
			s2[n++] = '*';
			p[0] = 1;
			int mx = 0;
			long long ans = 0;
			for(int i=1;i<n-1;i++)
			{
				p[i] = 1;
				if(mx + p[mx] - 1 > i)
					p[i] = min(p[mx-(i-mx)],mx+p[mx]-i);
				while(s2[i+p[i]] == s2[i-p[i]])
				{
					p[i]++;
				}
				if(i+p[i]-1 > mx+p[mx]-1)
					mx = i;
				ans += p[i]/2;
			}
			printf("%lld\n",ans);
		}
	} 
	return 0;
} 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值