zoukankan      html  css  js  c++  java
  • 【Codeforces Global Round 7 D2】Prefix-Suffix Palindrome (Hard version)

    题目链接

    链接

    翻译

    让你选择字符串 (s) 的一个前缀和一个后缀(可以为空), 然后拼成一个字符串。

    要求这个字符串得是一个回文串,且这个字符串的长度不能超过原串 (s) 的前提下最长。

    输出这个字符串, hard 版本,长度小于等于 (10^6)

    题解

    接上文

    现在的问题相当于要求从头部开始的连续回文串长 还是 以最后一个字符结尾的回文串长。

    分开两步做,开头连续的情况,则将 (s) 转化为 s+"#"+reverse(s) 其中 reverse 为翻转操作。

    那么现在相当于要找一个最大的数字 (X) 使得 (s[1..X] == s[len-X+1,len])

    这不就是 (KMP)(f) 数组吗。。所以对新的 (s) 做一下 (KMP)(f[len]) 的值就是所求的 (X) 了。

    然后把原始的 (s) 反向一下,再进行上述的井号拼接操作,求 (f) 同样可以得到一个 (X')

    比较一下 (X)(X') 谁大,就说明头部或尾部的回文串比较长,对应输出就好。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N = 1e6;
    
    int T;
    string s,pres,afters;
    int f[2*N+10];
    
    int main(){
       // freopen("C://1.cppSourceProgram//rush.txt","r",stdin);
        ios::sync_with_stdio(0),cin.tie(0);
        cin >> T;
        while (T--){
            cin >> s;
            int l = 0,r = s.length()-1;
            while (l<=r && s[l] == s[r]){
                l++;r--;
            }
            if (l > r){
                cout << s << endl;
                continue;
            }
            pres ="";
            afters ="";
            if (0<l){
                pres = s.substr(0,l);
            }
            if (r+1<(int)s.length()){
                afters = s.substr(r+1);
            }
            if (l<r+1){
                s = s.substr(l,r-l+1);
            }else{
                cout << s << endl;
                continue;
            }
            string copyRawS = s;
    
            reverse(s.begin(),s.end());
            s = copyRawS + "#" + s;
            int len = s.length();
    
            //开始做 KMP
            f[0] = 0;f[1] = 0;
            int j;
            for (int i = 1;i < len; i++){
                j = f[i];
                while (j > 0 && s[i] != s[j]){
                    j = f[j];
                }
                f[i+1] = j + (s[i]==s[j]?1:0);
            }
            //KMP 中 f[i+1]存的是以 i 这个字符结尾的后缀和字符串的前缀的最长公共前缀。
            //step1 求出前后最长公共前缀1
    
            int ma1 = f[len];
    
            //然后求从最后一个字符开始的回文串
            //step 2: 先得到对应的井号形式字符串
            s = copyRawS;
            reverse(s.begin(),s.end());
            s = s + "#" + copyRawS;
    
            //step 3: 同样求出f数组
            f[0] = 0;f[1] = 0;
            for (int i = 1;i < len; i++){
                j = f[i];
                while (j > 0 && s[i]!=s[j]){
                    j = f[j];
                }
                f[i+1] = j + (s[i]==s[j]?1:0);
            }
    
            //step 4: 求出前后最长公共前缀2
            int ma2 = f[len];
    
            //step 5: 判断从前面开始比较长 还是从后面开始比较长, 得到中间部分的回文串。
            string midAns;
    
            //如果中间没有回文
            if (ma1 == 0 && ma1 == ma2){
                midAns = "";
            }else
            //前面比较长
            if (ma1 > ma2){
                midAns = copyRawS.substr(0,ma1);
            }
            //后面比较长
            else{
                int lenRaw = copyRawS.length();
                midAns = copyRawS.substr(lenRaw-ma2,ma2);
            }
    
            //step 6 输出三个部分答案
            cout << (pres+midAns+afters) << endl;
        }
        return 0;
    }
    
  • 相关阅读:
    正确解读free -m
    linux命令总结之traceroute命令
    OSI七层模型详解
    Linux运维七:网络基础
    python contextlib 上下文管理器
    Django扩展自定义manage命令
    Elasticsearch分片、副本与路由(shard replica routing)
    EsRejectedExecutionException排错与线程池类型
    python重试(指数退避算法)
    Redis实现分布式锁
  • 原文地址:https://www.cnblogs.com/AWCXV/p/14126797.html
Copyright © 2011-2022 走看看