HDU6599多校第二场 I Love Palindrome String(回文树+马拉车)
I Love Palindrome String
Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 818 Accepted Submission(s): 329
Problem Description
You are given a string S=s1s2..s|S| containing only lowercase English letters. For each integer i∈[1,|S|] , please output how many substrings slsl+1...sr satisfy the following conditions:
∙ r−l+1 equals to i.
∙ The substring slsl+1...sr is a palindrome string.
∙ slsl+1...s⌊(l+r)/2⌋ is a palindrome string too.
|S| denotes the length of string S.
A palindrome string is a sequence of characters which reads the same backward as forward, such as madam or racecar or abba.
∙ r−l+1 equals to i.
∙ The substring slsl+1...sr is a palindrome string.
∙ slsl+1...s⌊(l+r)/2⌋ is a palindrome string too.
|S| denotes the length of string S.
A palindrome string is a sequence of characters which reads the same backward as forward, such as madam or racecar or abba.
Input
There are multiple test cases.
Each case starts with a line containing a string S(1≤|S|≤3×105) containing only lowercase English letters.
It is guaranteed that the sum of |S| in all test cases is no larger than 4×106.
Each case starts with a line containing a string S(1≤|S|≤3×105) containing only lowercase English letters.
It is guaranteed that the sum of |S| in all test cases is no larger than 4×106.
Output
For each test case, output one line containing |S| integers. Any two adjacent integers are separated by a space.
Sample Input
abababa
Sample Output
7 0 0 0 3 0 0
Source
题意:给你一个字符串,然后叫你求,对于每一个长度i-len,问有多少个,回文串的前一半也是回文串。
这题的话,我们需要用到一个回文树(也叫回文自动机)来处理,回文树可以在O(nlogm)(其实logm很小,可以忽略,复杂度相当与O(n))的时间里找到所以的回文串,并统计
出每一个回文串的长度
了解回文树的都知道,回文树的没一个结点代表的都是一个回文串,由于我们要判断这个回文串的一半是否为回文串,这么我们可以用hash或manachar处理,我选择的用manachar。
在回文树的结点信息上再添加一个保存在原来字符串位置的区间(l,r),为什么要添加这个呢?因为我们用manachar来判断的回文的话需要用到回文中心,这里我们需要知道的是原回文子字符串str[l~r)]在转换后字符串的回文中心位置(l+r+2),这时我们只需要判断这个回文串的一半是否回文manachar_len[l +(l+r)/2 + 2]的长度是否满足大于等于它的回文长度/2.
回文树不懂的话,代码有注释,可能看着相对容易理解一点(感觉fail数组那不好理解,学了好久好久)
这里用hash判断回文的话似乎更简单一些,比如当前选择的结点为ababa我们用hash判断长度为奇数判断hash[l,mid]和hash[mid, r]是否相等就好了
偶长度判断 hash[l,mid]和hash[mid+1, r]
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int MAXN = 300005; 4 char tmp[MAXN * 2]; 5 int manachar_len[MAXN*2]; 6 struct Palindromic_Tree 7 { 8 int next[MAXN][26]; 9 int fail[MAXN];///当前节点的最长回文后缀串的结点(不包括自己),也就是匹配不成功应该跳转的地方, 10 int cnt[MAXN]; 11 int len[MAXN];///回文串的长度 12 int num[MAXN]; 13 int s[MAXN];///加入的字符 14 int p, n, last;///n字符的指针,p表示结点,一个节点代表一个回文串,last指向新添加一个字母后所形成的最长回文串表示的节点 15 struct node 16 { 17 int l, r; 18 } P[MAXN]; 19 int newNode(int length) 20 { 21 for (int i = 0; i < 26; i++) 22 next[p][i] = 0; 23 num[p] = cnt[p] = 0;///记录信息 24 len[p] = length; 25 P[p].l = 0; 26 P[p].r = 0; 27 return p++; 28 } 29 void init() 30 { 31 p = 0; 32 newNode(0);//0 号节点代表偶数根,长度为0 , 1号节点代表奇根,长度为 -1 33 newNode(-1); 34 last = n = 0;///刚开始时,所有节点指向0号节点 35 fail[0] = 1;///0号节点的fail 指针指向 1(奇根) 号节点 36 s[0] = -1;///开头放一个字符集中没有的字符,减少特判 37 } 38 int get_fail(int x)//找到第一个使得S[n - len[last] - 1] == S[n]的last 39 { 40 while(s[n - len[x] - 1] != s[n]) 41 x = fail[x]; 42 return x; 43 } 44 void add(int c) 45 { 46 int x = c-'a'; 47 s[++ n] = x; 48 int cur = get_fail(last);///cur节点相当于now节点的父亲结点 cur->now 49 if(!next[cur][x]) 50 { 51 int now = newNode(len[cur] + 2); 52 fail[now] = next[ get_fail( fail[cur] ) ][ x ];///fail[cur]得到cur的最长回文后缀, 53 ///再通过这个回文后缀找到使得S[n - len[last] - 1] == S[n]的last结点 54 ///比如abbaabba cur指向的结点为bbaabb,这个节点的最长回文后缀为bb,那么看bb['a'[bb]'x'] 55 ///看'x'是否和'a'相等,如果相等,那么fail指正指向['a'[bb]'x']这个节点,否则的话就不断去缩小bb 56 ///回文后缀,直到到达 1 号节点,fail 指向长度为 1 的 'x' 节点; 57 next[cur][x] = now; 58 num[now] = num[fail[now]] + 1; 59 } 60 last = next[cur][x]; 61 P[last].l = n -1 - len[last] + 1; 62 P[last].r = n - 1; 63 cnt[last] ++; 64 } 65 void count() 66 { 67 for(int i = p - 1; i >= 0; i --) 68 { 69 /// printf("%d\n", cnt[i]); 70 cnt[fail[i]] += cnt[i]; 71 } 72 } 73 } AC; 74 75 int init(char st[]) 76 { 77 int i,str_len=strlen(st); 78 tmp[0]='@';///加一个特殊字符,防止越界 79 for(int i = 1; i <= 2*str_len ; i+=2) 80 { 81 tmp[i]='#'; 82 tmp[i+1]=st[i/2]; 83 } 84 tmp[2*str_len+1]='#'; 85 tmp[2*str_len+2]='$';///字符串结尾加一个字符,防止越界 86 return 2*str_len+1;///返回转换字符串的长度 87 } 88 89 void manachar(char st[],int str_len) 90 { 91 int p_mx=0,ans=0,po=0;///p_mx即为“当前”计算回文串最右边字符的最大值,po为当前该回文字符串的中心 92 for(int i=1; i<=str_len; i++) 93 { 94 if(i<p_mx) 95 manachar_len[i]=min(p_mx-i,manachar_len[2*po-i]);///po对应最大回文子串中心点的位置 96 else 97 manachar_len[i]=1; 98 while(st[i-manachar_len[i]]==st[i+manachar_len[i]]) 99 manachar_len[i]++; 100 if(manachar_len[i]+i>p_mx) 101 { 102 po=i; 103 p_mx=i+manachar_len[i]; 104 } 105 ans=max(ans,manachar_len[i]); 106 } 107 // return ans-1;///返回manachar_len[i]中的最大值减一即为原串的最长回文子串的长度 108 } 109 char str[MAXN]; 110 int len_ans[MAXN]; 111 int main() 112 { 113 while(~scanf("%s",str)) 114 { 115 int len_tmp = init(str); 116 manachar(tmp, len_tmp); 117 memset(len_ans, 0,sizeof(len_ans)); 118 int len = strlen(str); 119 AC.init(); 120 for(int i = 0; i < len; i++) 121 { 122 AC.add(str[i]); 123 } 124 AC.count(); 125 long long ans = 1; 126 for(int i = 0; i < AC.p; i++) 127 { 128 if(manachar_len[AC.P[i].l+(AC.P[i].l+AC.P[i].r)/2+2] - 1 >= AC.len[i]/2) 129 { 130 len_ans[AC.len[i]] += AC.cnt[i]; 131 } 132 } 133 for(int i = 1; i <= len; i++) 134 { 135 if(i == 1 ) 136 printf("%d",len_ans[i]); 137 else 138 printf(" %d",len_ans[i]); 139 } 140 puts(""); 141 } 142 143 return 0; 144 }