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(" "); #define Debug printf("this_ok "); 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 ",a); #define print_int(a,b) if(b == 0) printf("%d ",a); else printf("%d ",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 ",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 ",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 ",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(" "); #define Debug printf("this_ok "); 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 ",a); #define print_int(a,b) if(b == 0) printf("%d ",a); else printf("%d ",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 ",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 */