本文主要是介绍【CF】团队训练赛2 J-Palindrome Reversion 题解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
传送门:Palindrome Reversion
标签:字符串
题目大意
规定一个操作:选择字符串中的一段区间[l,r]并使其翻转。现在给出一个字符串s,你要判断能否通过一次操作使其变为回文串。
输入:一个字符串,其长度不超过1e5。
输出:可以则输出l和r,否则输出"-1 -1"。
算法分析
- 题目的意思可以说是相当的简单,但正所谓大道至简,容易读懂不代表容易做。我们先要知道回文串的特征:翻转后与翻转前完全相同(这里指的是整个字符串reverse)。那么我们可以大胆地猜测一个结论:原字符串首尾相同的部分必然不被包含在操作区间[l,r]中。因为如果有一部分在[l,r]之间,要想保证操作后整个串回文,就要让替换这部分的字符与之相同,那么替换就毫无意义。
- 也就是说我们可以先通过遍历找到第一个不满足首尾相同的字符,假设其下标是i,这样一来区间[l,r]要么包括i,要么包括len-i+1(这里len为字符串长度)。因为此时的字符串第一个字符和最后一个字符不同,所以需要换掉第一个或者最后一个字符来使得它们相同,但是又要想到同时替换并不会改变它们的对应关系。
- 所以现在问题变得简单了起来,只要在去掉了首尾相同部分的新字符串上从前往后枚举区间,再从后往前枚举区间,并判断翻转后整体是否回文,就能得出方案的可行性。不过实现起来有点难度,因为要正反各处理一次哈希,还要用到哈希值的截取、拼接等操作,稍有不慎就会出bug。不过有一个小技巧能让本题轻松一半,就是在正向枚举完区间后将整个字符串翻转,然后再次正向枚举区间,这样就和反向枚举区间的作用是一样的了。
- 最后还要特判一下原字符串本身就回文的情况,这时直接输出1和len即可。只要推出了最初的结论,这题的思路就很简单,难点主要在于实现。以上算法总体时间复杂度为预处理双向哈希的复杂度,即O(n)。
代码实现
#include <iostream>
using namespace std;
#include <algorithm>
#include <cstring>
long long len,hal[100005],har[100005],base[100005]={1LL};
const int mod=1e9+19;
const long long P=13031LL;
char str[100005],str1[100005];
void init(){int i;hal[0]=str[0];for(i=1;i<len;i++)hal[i]=(hal[i-1]*P+str[i])%mod;har[len-1]=str[len-1];for(i=len-2;i>=0;i--)har[i]=(har[i+1]*P+str[i])%mod;
}
long long getl(int l,int r){if(l>r)return 0;if(!l)return hal[r];return (hal[r]-1LL*hal[l-1]*base[r-l+1]%mod+mod)%mod;
}
long long getr(int l,int r){if(l>r)return 0;if(r==len-1)return har[l];return (har[l]-1LL*har[r+1]*base[r-l+1]%mod+mod)%mod;
}
int main(){int i,j,x,len1;bool flag=0;ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);for(i=1;i<=1e5;i++)base[i]=base[i-1]*P%mod;cin>>str1;len1=strlen(str1);for(j=0;j<len1&&str1[j]==str1[len1-j-1];j++);if(j==len1){cout<<1<<' '<<len1;return 0;}for(i=j;i<len1-j;i++)str[i-j]=str1[i];len=len1-2*j;init();x=len/2;for(i=0;i<len;i++)if(i<x){if(getr(x,len-1)==(getr(0,i)*base[x-1+(len&1)-i]%mod+getl(i+1,x-1+(len&1)))%mod){cout<<1+j<<' '<<i+1+j;return 0;}}else{if(getr(i-x+1,i)==(getl(0,i-x-(len&1))+getr(i+1,len-1)*base[i-x-(len&1)+1]%mod)%mod){cout<<1+j<<' '<<i+1+j;return 0;}}reverse(str,str+len);init();x=len/2;for(i=0;i<len;i++)if(i<x){if(getr(x,len-1)==(getr(0,i)*base[x-1+(len&1)-i]%mod+getl(i+1,x-1+(len&1)))%mod){cout<<len1-(i+1+j)+1<<' '<<len1-(1+j)+1;return 0;}}else{if(getr(i-x+1,i)==(getl(0,i-x-(len&1))+getr(i+1,len-1)*base[i-x-(len&1)+1]%mod)%mod){cout<<len1-(i+1+j)+1<<' '<<len1-(1+j)+1;return 0;}}cout<<-1<<' '<<-1;
}
这篇关于【CF】团队训练赛2 J-Palindrome Reversion 题解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!