Prefix-Suffix Palindrome

D1,

题意:

输入T组数据;

每组数据一行字符串str;

现在让你选str的前缀和后缀,使得前缀+后缀是回文串,且使它的长度是最长的。

注意:前缀或后缀的字符串 可以为空。

///样例解释
/*
5
a
abcdfdcecba
abbaxyzyx
codeforces
acbba
*/
/*
a
abcdfdcba  abcdfd是给定字符串的前缀,cba是字符串的后缀
xyzyx   此前缀为空,xyzyx是字符串的后缀
c
abba  a是给定字符串的前缀+bba是给定字符串的后缀
*/

 做法:

我们先考虑给定字符串的前缀和后缀相匹配的长度。

比如像给定了字符串“abcdefecba”

我们可以发现他的前缀和后缀相匹配的长度为3,即“abc” 与“cba”是想匹配的。(这里的相匹配的意思就是找到最长的前缀 + 相同长度的后缀为回文串;)

那我们考虑剩下的字符串“defe” 在主串中的下标为:4567;

因为答案为前缀+后缀的形式;

所以,我们需要考虑(4,4)和(4,5)和(4,6)和(4,7)和(5,7)和(6,7)和(7,7)

这些区间是否可以构成回文串,并记录下最长的那个回文串;

#include"stdio.h"
#include"string.h"
#include"stack"
#include"map"
#include"math.h"
#include"vector"
#include"queue"
#include"algorithm"
using namespace std;
#define OK printf("\n");
#define Debug printf("this_ok\n");
typedef long long ll;
#define scanll(a,b) scanf("%lld%lld",&a,&b);
#define scanl(a) scanf("%lld",&a);
#define printl(a,b) if(b == 0) printf("%lld ",a); else printf("%lld\n",a);
#define print_int(a,b) if(b == 0) printf("%d ",a); else printf("%d\n",a);
const ll mod=998244353;
typedef pair<int,int> PII;
inline int read(){
    int s = 0, w = 1; char ch = getchar();
    while(ch < '0' || ch > '9')   { if(ch == '-') w = -1; ch = getchar(); }
    while(ch >= '0' && ch <= '9') { s = (s << 3) + (s << 1) + (ch ^ 48); ch = getchar(); }
    return s * w;
}

const int N = 201010;
const int dirx[4] = {0,1,0,-1};
const int diry[4] = {-1,0,1,0};
int n,k,len;
int a[N];
char str[N];
vector<int>Q1,Q2;
int check(int l,int r){ ///判断字符串的(l,r)区间是否为回文串,若是返回1,否则,返回0;
    while(l < r){
        if(str[l] != str[r]) return 0;
        l ++; r --;
    }
    return 1;
}
int main()
{
     int T = read();
     while(T --){
         scanf("%s",str + 1);
         int loc = 0;len = strlen(str + 1);
         int l = 1,r = len;
         while(l <= r){ ///此函数可以找到前缀和后缀的最长长度。
            if(str[l] != str[r]) break;
            l ++; r --;
         }

         if(l > r) {  ///如果l>r了,那其本身就是回文串,直接输出即可
            printf("%s\n",str + 1); continue;
         }

         l --; r ++; ///注意,str[l] != str[r]; 所以l--,r++;
         int L1 = l + 1,R1 = r - 1; ///L1为剩下字符串的最左下标,R1为剩下字符串的最右下标
        // printf("L1 = %d R1 = %d\n",L1,R1);
         int mid1 = 0,mid2 = 0;
         for(int i = L1; i <= R1; i ++){ ///暴力枚举最左下标所构成的区间,判断是否为回文串,并将值保存在mid1中;
             if(check(L1,i) == 0) continue;
             mid1 = i;
         }
         for(int i = R1; i >= L1; i --){ ///暴力枚举最右下标所构成的区间,判断是否为回文串,并将值保存在mid2中
            if(check(i,R1) == 0) continue;
            mid2 = i;
         }
        // printf("mid1 = %d mid2= %d\n",mid1,mid2);
         int len1 = mid1 - L1 + 1; ///计算以最左下标构成的区间最长为多少
         int len2 = R1 - mid2 + 1;///计算最右下标构成的区间长度最长为多少
         if(len1 >= len2) { ///比较长度,输入较长那一部分;
            for(int i = 1; i <= l; i ++) printf("%c",str[i]);
            for(int i = L1; i <= mid1; i ++) printf("%c",str[i]);
            for(int i = r; i <= len; i ++) printf("%c",str[i]);
            OK continue;
         } else  {
             for(int i = 1; i <= l; i ++) printf("%c",str[i]);
            for(int i = mid2; i <= R1; i ++) printf("%c",str[i]);
            for(int i = r; i <= len; i ++) printf("%c",str[i]);OK continue;
         }
     }
}

/*
5
2
1 4 5 5
5 2 1 5 5 3
*/

 

D2,数据范围比D1大;(字符串hash)

从而我们不能去枚举区间;

所以我们需要考虑的是,如何在O(n)的时间复杂度中,找到最长的回文串;

基本思路和上面一样;

不同之处:我们在处理为匹配的字符串的时候,我们可以先把他的字符串hash值算出来;即上面所讲样例:字符串“defe”,我们把其hash值算出来;

然后枚举长度,判断这个长度是否为回文串;

#include"stdio.h"
#include"string.h"
#include"stack"
#include"map"
#include"math.h"
#include"vector"
#include"queue"
#include"algorithm"
using namespace std;
#define OK printf("\n");
#define Debug printf("this_ok\n");
typedef long long ll;
typedef unsigned long long ull;
#define scanll(a,b) scanf("%lld%lld",&a,&b);
#define scanl(a) scanf("%lld",&a);
#define printl(a,b) if(b == 0) printf("%lld ",a); else printf("%lld\n",a);
#define print_int(a,b) if(b == 0) printf("%d ",a); else printf("%d\n",a);
const ull mod1=19260811;
const ull mod2=999998639;
ull base = 2333;
typedef pair<int,int> PII;
inline int read(){
    int s = 0, w = 1; char ch = getchar();
    while(ch < '0' || ch > '9')   { if(ch == '-') w = -1; ch = getchar(); }
    while(ch >= '0' && ch <= '9') { s = (s << 3) + (s << 1) + (ch ^ 48); ch = getchar(); }
    return s * w;
}
///调式的时候一直把ull的格式符弄成了%u,导致调式就一直莫名奇妙错失%ull
const int N = 2010100;
const int dirx[4] = {0,1,0,-1};
const int diry[4] = {-1,0,1,0};
int n,k,len;
int a[N];
char str[N];
ull a1[N],c1[N];
ull b1[N];

int mark = 1;int Len;
int check(int l,int r){ ///判断区间[l,r]是否为回文串,与D1不同,这里是使用字符串的hash进行判断,并且要注意长度为奇数和偶数时的区别;
    int len = r - l + 1; int mid;
    ull A1,B1;
    if(len % 2 == 1){
        mid = l + (len / 2);
         A1 = (a1[mid - 1] - a1[l - 1] * c1[mid - l] % mod2 + mod2) % mod2;
         B1 = (b1[mid + 1] - b1[r + 1] * c1[r - mid] % mod2 + mod2) % mod2;
        if(A1 == B1) return 1;
    } else  {
         mid = l + (len / 2) - 1;
         A1 = (a1[mid] - a1[l - 1] * c1[mid - l + 1] % mod2 + mod2) % mod2;
         B1 = (b1[mid + 1] - b1[r + 1] * c1[r - mid] % mod2 + mod2) % mod2;
        if(A1 == B1) return 1;
    }
    return 0;
}
int main()
{
     int T = read();
     while(T --){
         scanf("%s",str + 1);
         int loc = 0;len = strlen(str + 1);
         int l = 1,r = len;
         while(l <= r){
            if(str[l] != str[r]) break;
            l ++; r --;
         }
         if(l > r) {
            printf("%s\n",str + 1); continue;
         }
         l --; r ++;
         
         int L1 = l + 1,R1 = r - 1;
         c1[0] = 1; Len = R1 - L1 + 1;
         a1[Len] = a1[Len + 1] = 0;
         b1[Len] = b1[Len + 1] = 0;
         for(int i = L1; i <= R1; i ++){ ///这里把区间[L1,R1]的hash值算出来(从左到右),注意,我下标是从1开始的;
             a1[i - L1 + 1] = (a1[i - L1] * base + (str[i] - 'a' + 1)) % mod2;;
             c1[i - L1 + 1] = c1[i - L1] * base % mod2;;
         }
         for(int i =  R1; i >= L1; i --){///把区间[L1,R1]的反向hash值算出来(从右到左);
            b1[i - L1 + 1] = (b1[i - L1 + 2] * base + (str[i] - 'a' + 1)) % mod2;
         }
         
         int mid1 = 0,mid2 = Len;
         for(int i = 1; i <= Len; i ++){ ///枚举长度
            if(check(1,i) == 0) continue;
            mid1 = i;
         }
         for(int i = Len; i >= 1; i --){ ///枚举长度
            if(check(i,Len) == 0) continue;
            mid2 = i;
         }
         int len1 = mid1; int len2 = Len - mid2 + 1;
         if(len1 >= len2) {
            for(int i = 1; i <= l; i ++) printf("%c",str[i]);
            for(int i = L1; i <= mid1 + l; i ++) printf("%c",str[i]);
            for(int i = r; i <= len; i ++) printf("%c",str[i]);
            OK continue;
         } else  {
             for(int i = 1; i <= l; i ++) printf("%c",str[i]);
            for(int i = R1 - len2 + 1; i <= R1; i ++) printf("%c",str[i]);
            for(int i = r; i <= len; i ++) printf("%c",str[i]);OK continue;
         }
     }
}

/*
5
2
1 4 5 5
5 2 1 5 5 3
*/

 

posted @ 2020-03-20 17:28  风生  阅读(251)  评论(0编辑  收藏  举报