翻转一个区间使得字符串成为回文串

题目

子串翻转回文串 http://acm.zzuli.edu.cn/problem.php?id=2700

题目描述

给一个串 s=s1s2⋯sn,你可以选定其一个非空子串,然后将该子串翻转。具体来说,若选定的子串区间为 [l,r](1≤l≤r≤n),则翻转后该串变为 s1s2⋯sl−1srsr−1⋯slsr+1⋯sn。
请你回答仅通过一次上述操作后,s 是否能变成回文串。串 s 是回文串,当且仅当它从左至右读出与从右至左读出完全相同,即 s1s2⋯sn=snsn−1⋯s1

输入

注意:本题包含多组测试数据。
第一行包含一个整数 T(1≤T≤5×10^5),表示数据组数。
接下来的 T 行,每行包含一个仅由英文小写字母组成的字符串 s,含义见题目描述,且串长 ∣s∣ 满足1≤∣s∣≤5×10^5。
保证字符串总长 ∑∣s∣不超过 5×10^5。

输出

对于每组测试数据,输出一行一个字符串,若仅通过一次操作后 s 能变成回文串,则输出 Yes,否则输出 No,大小写不敏感。

样例输入

4
abba
bacad
abacbaa
aabadcdca

样例输出

Yes
No
Yes
Yes

提示

第一组数据中,abba 翻转带下划线的子串得到回文串 abba。
第二组数据中,无论如何翻转子串,都不能得到回文串。
第三组数据中, abacbaa翻转带下划线的子串得到回文串 aabcbaa。
第四组数据中, aabadcdca翻转带下划线的子串得到回文串 acdabadca。

思路

我们首先得到一个结论:

我们把s[1]和s[n]匹配。
s[2]和s[n-1]匹配。
...
s[pL]是第一个和s[pR]不相等的。
那么翻转的一个端点一定pL或者pR。那么我们枚举一下,再枚举另外一个端点。
这个时候复杂度为O(n),我们再考虑怎么处理翻转和判断回文。

我们维护一个反串。设原串为1,反串为2。那么1串翻转[L, R]和2串翻转[n-r+1, n-r+1+r-l]得到的串相等的话就说明翻转后
得到的是回文串。我们判断相等这里只能用哈希值。那怎么得到翻转的哈希值。这里又用到了反串的哈希。
我们把1串分成3部分。中间翻转[L, R]的部分的哈希值不就是反串的[n-r+1, n-r+1+r-l]的哈希值。

所以维护2个哈希就可以了。

#include <bits/stdc++.h>
#define LL long long
using namespace std;

const int maxn=500005;
struct Hash_char{
    const LL mod=998244353, base=131;
    LL p[maxn], g[maxn];
    void getp(){
        p[0]=1;
        for(int i=1; i<maxn; i++){
            p[i]=p[i-1]*base%mod;
        }
    }
    LL Hash(char s[]){
        int len=strlen(s+1);
        g[0]=0, g[1]=s[1];
        for(int i=2; i<=len; i++){
            g[i]=(g[i-1]*base+s[i])%mod;
        }
        return g[len];
    }
    LL getLR(int l, int r){//得到s[l]-s[r]的hash值
        if(l>r) return 0;
        LL ans=((g[r]-g[l-1]*p[r-l+1])%mod+mod)%mod;
        return ans;
    }

}T[2];

int n;
LL strfz(int pos, int l, int r){//pos:正反串,翻转区间l, r。
    LL ans=T[pos].getLR(1, l-1)*T[pos].p[n-l+1]%T[pos].mod;
    LL res=T[pos^1].getLR(n-r+1, n-r+1+r-l)*T[pos].p[n-r]%T[pos].mod;
    ans=ans+res+T[pos].getLR(r+1, n);
    return ans%T[pos].mod;
}

char s[maxn], s1[maxn];
int main(){

    T[0].getp(); T[1].getp();
    int t; scanf("%d", &t);
    while(t--){
        scanf("%s", s+1);
        n=strlen(s+1);
        for(int i=1; i<=n; i++){
            s1[i]=s[n-i+1];
        }
        s1[n+1]=0;
        T[0].Hash(s); T[1].Hash(s1);
        int pl=1, pr=n;
        while(s[pl]==s[pr]&&pl<=pr){
            pl++, pr--;
        }
        int f=0;
        for(int i=pl; i<=n; i++){
            if(strfz(0, pl, i)==strfz(1, n-i+1, n-i+1+i-pl)){
                f=1; break;
            }
        }
        for(int i=pr; i>=1; i--){
            if(strfz(0, i, pr)==strfz(1, n-pr+1, n-pr+1+pr-i)){
                f=1; break;
            }
        }
        if(f||pl>=pr) printf("Yes\n");
        else printf("No\n");
    }

    return 0;
}
posted @ 2020-12-02 10:28  liweihang  阅读(530)  评论(0编辑  收藏  举报
Live2D