UVA - 11475 Extend to Palindrome —— 字符串哈希 or KMP or 后缀数组
题目链接:https://vjudge.net/problem/UVA-11475
题意:
给出一个字符串,问在该字符串后面至少添加几个字符,使得其成为回文串,并输出该回文串。
题解:
实际上是求该字符串的“最长回文后缀”,有多种做法,其中用字符串哈希的方法最方便而且速度最快。
字符串哈希:
从字符串的最后一个字符开始,往前进行计算两个哈希值,其中一个按“先高位后低位”的方法计算,另一个按“先低位后高位”的方法计算。如果在某个位置,两个哈希值相等,那么表明该后缀为回文串,求最长的那个即可。
代码如下:
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <vector> 6 #include <cmath> 7 #include <queue> 8 #include <stack> 9 #include <map> 10 #include <string> 11 #include <set> 12 using namespace std; 13 typedef long long LL; 14 const int INF = 2e9; 15 const LL LNF = 9e18; 16 const int MOD = 1e9+7; 17 const int MAXN = 2e5+100; 18 19 const LL seed = 13331; 20 char str[MAXN]; 21 int main() 22 { 23 while(scanf("%s",str)!=EOF) 24 { 25 int len = strlen(str); 26 int pos = len-1; 27 LL hash1 = 0, hash2 = 0, s = 1; 28 for(int i = len-1; i>=0; i--) 29 { 30 hash1 *= seed, hash1 += str[i]; 31 hash2 += str[i]*s, s *= seed; 32 if(hash1==hash2) 33 pos = i-1; 34 } 35 for(int i = 0; i<len; i++) putchar(str[i]); 36 for(int i = pos; i>=0; i--) putchar(str[i]); 37 putchar('\n'); 38 } 39 }
KMP:
1. 根据next数组的next[len]即为字符串前缀与后缀的最长匹配(不包括重叠的情况),可以将字符串的逆串接在其前面,中间用一个分割符隔开,得到新串,求出新串的next数组
1.1 逆串放在前面是因为用逆串的前缀去匹配原串的后缀。
1.2 中间加个分隔符是用于防止逆串匹配到逆串那里去,即防止过界。
2. 假设新串的长度为len, 那么next[len]即为可节省的最大长度,因而只需添加len-next[len]个字符即可。
代码如下:
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <vector> 6 #include <cmath> 7 #include <queue> 8 #include <stack> 9 #include <map> 10 #include <string> 11 #include <set> 12 using namespace std; 13 typedef long long LL; 14 const int INF = 2e9; 15 const LL LNF = 9e18; 16 const int MOD = 1e9+7; 17 const int MAXN = 2e5+100; 18 19 char x[MAXN]; 20 int Next[MAXN]; 21 void get_next(char x[], int m) 22 { 23 int i, j; 24 j = Next[0] = -1; 25 i = 0; 26 while(i<m) 27 { 28 while(j!=-1 && x[i]!=x[j]) j = Next[j]; 29 Next[++i] = ++j; 30 } 31 } 32 33 char str[MAXN]; 34 int main() 35 { 36 while(scanf("%s",str)!=EOF) 37 { 38 int len = strlen(str); 39 int m = 2*len+1; 40 for(int i = 0; i<len; i++) 41 { 42 x[len-1-i] = str[i]; 43 x[len+1+i] = str[i]; 44 } 45 x[len] = '$'; x[m] = 0; 46 get_next(x, m); 47 48 int pos = len-Next[m]-1; 49 for(int i = 0; i<len; i++) putchar(str[i]); 50 for(int i = pos; i>=0; i--) putchar(str[i]); 51 putchar('\n'); 52 } 53 }
后缀数组:
1.将逆串接在原串的后面,中间用一个分隔符隔开,得到新串。求出新串的后缀数组。
2.枚举原串在新串中的每一个位置i,该位置代表着新串的一个后缀i,设逆串首字符在原串中的位置为j,同样代表着新串的一个后缀j。加入后缀i与后缀j个最长公共前缀lcp满足:i+lcp==len,即表明最长公共前缀已经延伸到原串的串尾,所以该原串的后缀i为回文串。同样,求出最长的那个即可。
代码如下:
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <vector> 6 #include <cmath> 7 #include <queue> 8 #include <stack> 9 #include <map> 10 #include <string> 11 #include <set> 12 using namespace std; 13 typedef long long LL; 14 const int INF = 2e9; 15 const LL LNF = 9e18; 16 const int MOD = 1e9+7; 17 const int MAXN = 2e5+100; 18 19 bool cmp(int *r, int a, int b, int l) 20 { 21 return r[a]==r[b] && r[a+l]==r[b+l]; 22 } 23 24 int r[MAXN], sa[MAXN], Rank[MAXN], height[MAXN]; 25 int t1[MAXN], t2[MAXN], c[MAXN]; 26 void DA(int str[], int sa[], int Rank[], int height[], int n, int m) 27 { 28 n++; 29 int i, j, p, *x = t1, *y = t2; 30 for(i = 0; i<m; i++) c[i] = 0; 31 for(i = 0; i<n; i++) c[x[i] = str[i]]++; 32 for(i = 1; i<m; i++) c[i] += c[i-1]; 33 for(i = n-1; i>=0; i--) sa[--c[x[i]]] = i; 34 for(j = 1; j<=n; j <<= 1) 35 { 36 p = 0; 37 for(i = n-j; i<n; i++) y[p++] = i; 38 for(i = 0; i<n; i++) if(sa[i]>=j) y[p++] = sa[i]-j; 39 40 for(i = 0; i<m; i++) c[i] = 0; 41 for(i = 0; i<n; i++) c[x[y[i]]]++; 42 for(i = 1; i<m; i++) c[i] += c[i-1]; 43 for(i = n-1; i>=0; i--) sa[--c[x[y[i]]]] = y[i]; 44 45 swap(x, y); 46 p = 1; x[sa[0]] = 0; 47 for(i = 1; i<n; i++) 48 x[sa[i]] = cmp(y, sa[i-1], sa[i], j)?p-1:p++; 49 50 if(p>=n) break; 51 m = p; 52 } 53 54 int k = 0; 55 n--; 56 for(i = 0; i<=n; i++) Rank[sa[i]] = i; 57 for(i = 0; i<n; i++) 58 { 59 if(k) k--; 60 j = sa[Rank[i]-1]; 61 while(str[i+k]==str[j+k]) k++; 62 height[Rank[i]] = k; 63 } 64 } 65 66 int dp[MAXN][20], mm[MAXN]; 67 void initRMQ(int n, int b[]) 68 { 69 mm[0] = -1; 70 for(int i = 1; i<=n; i++) 71 dp[i][0] = b[i], mm[i] = ((i&(i-1))==0)?mm[i-1]+1:mm[i-1]; 72 for(int j = 1; j<=mm[n]; j++) 73 for(int i = 1; i+(1<<j)-1<=n; i++) 74 dp[i][j] = min(dp[i][j-1], dp[i+(1<<(j-1))][j-1]); 75 } 76 77 int RMQ(int x, int y) 78 { 79 if(x>y) swap(x, y); 80 x++; 81 int k = mm[y-x+1]; 82 return min(dp[x][k], dp[y-(1<<k)+1][k]); 83 } 84 85 char str[MAXN]; 86 int main() 87 { 88 while(scanf("%s", str)!=EOF) 89 { 90 int len = strlen(str); 91 int n = 2*len+1; 92 for(int i = 0; i<len; i++) 93 { 94 r[i] = str[i]; 95 r[n-1-i] = str[i]; 96 } 97 r[len] = '$'; r[n] = 0; 98 DA(r, sa, Rank, height, n, 128); 99 initRMQ(n, height); 100 101 int pos = len-1; 102 for(int i = 0; i<len; i++) 103 { 104 int lcp = RMQ(Rank[i], Rank[len+1]); 105 if(i+lcp==len) 106 { 107 pos = i-1; 108 break; 109 } 110 } 111 for(int i = 0; i<len; i++) putchar(str[i]); 112 for(int i = pos; i>=0; i--) putchar(str[i]); 113 putchar('\n'); 114 } 115 }