In an English class Nick had nothing to do at all, and remembered about wonderful strings called palindromes. We should remind you that a string is called a palindrome if it can be read the same way both from left to right and from right to left. Here are examples of such strings: «eye», «pop», «level», «aba», «deed», «racecar», «rotor», «madam».
Nick started to look carefully for all palindromes in the text that they were reading in the class. For each occurrence of each palindrome in the text he wrote a pair — the position of the beginning and the position of the ending of this occurrence in the text. Nick called each occurrence of each palindrome he found in the text subpalindrome. When he found all the subpalindromes, he decided to find out how many different pairs among these subpalindromes cross. Two subpalindromes cross if they cover common positions in the text. No palindrome can cross itself.
Let's look at the actions, performed by Nick, by the example of text «babb». At first he wrote out all subpalindromes:
• « b» — 1..1
• « bab» — 1..3
• « a» — 2..2
• « b» — 3..3
• « bb» — 3..4
• « b» — 4..4
Then Nick counted the amount of different pairs among these subpalindromes that cross. These pairs were six:
1. 1..1 cross with 1..3
2. 1..3 cross with 2..2
3. 1..3 cross with 3..3
4. 1..3 cross with 3..4
5. 3..3 cross with 3..4
6. 3..4 cross with 4..4
Since it's very exhausting to perform all the described actions manually, Nick asked you to help him and write a program that can find out the amount of different subpalindrome pairs that cross. Two subpalindrome pairs are regarded as different if one of the pairs contains a subpalindrome that the other does not.
Input
The first input line contains integer n (1 ≤ n ≤ 2·106) — length of the text. The following line contains n lower-case Latin letters (from a to z).
Output
In the only line output the amount of different pairs of two subpalindromes that cross each other. Output the answer modulo 51123987.
Examples
Input
4 babbOutput
6Input
2 aaOutput
2
题目大意:求所有相交的回文子串的个数。
解题思路:求回文子串,肯定要先Manacher我们可以发现判断相交要考虑多种情况,既有相交又有包含,难以成段计算但两子串不相交的条件只有一个,即A子串的右边界严格小于B子串的左边界,再用总个数相减即可我们用差分维护l[i],r[i]分别表示左/右边界恰好为i的个数,同时维护r[i]的前缀和,即可O(n)地更新答案
回文自动机思路:反着插一次,计算后缀和,再正着插一次,计算没有重复位置的字符串对,然后用总的减掉就好了,但是第一个测试点就超内存,一直写不过去,代码也会贴出,望大佬给出解答。
回文自动机:
/* @Author: Top_Spirit @Language: C++ */ //#include
#include #include #include #include #include using namespace std ; typedef unsigned long long ull ; typedef long long ll ; const int Maxn = 2 * 1e6 + 10 ; const int INF = 0x3f3f3f3f ; const double PI = acos(-1.0) ; const ull seed = 133 ; const ll MOD = 51123987 ; int n ; struct palindromic_tree{ int Next[Maxn][26]; //表示编号为i的节点表示的回文串在两边添加字符c以后变成的回文串的编号 int fail[Maxn]; //表示节点i失配以后跳转不等于自身的节点i表示的回文串的最长后缀回文串 int cnt[Maxn]; //表示节点i表示的本质不同的串的个数(建树时求出的不是完全的,最后count()函数跑一遍以后才是正确的) int num[Maxn]; //表示以节点i表示的最长回文串的最右端点为回文串结尾的回文串个数 int len[Maxn]; //表示编号为i的节点表示的回文串的长度(一个节点表示一个回文串) int S[Maxn]; //表示第i次添加的字符(一开始设S[0] = -1(可以是任意一个在串S中不会出现的字符)) int last; //指向新添加一个字母后所形成的最长回文串表示的节点 int n; //表示添加的字符个数 int p; //表示添加的节点个数 int newNode (int k){ for (int i = 0; i < 26; i++) Next[p][i] = 0 ; cnt[p] = 0 ; num[p] = 0 ; len[p] = k ; return p++ ; } void init(){ p = 0 ; newNode(0) ; newNode(-1) ; last = 0 ; n = 0 ; S[n] = -1 ; fail[0] = 1 ; } int get_fail (int x){ while (S[n - len[x] - 1] != S[n]) x = fail[x] ; return x ; } int add (int c){ c -= 'a' ; S[++n] = c ; int cur = get_fail(last) ; if (!Next[cur][c]){ int Now = newNode(len[cur] + 2) ; fail[Now] = Next[get_fail(fail[cur])][c] ; Next[cur][c] = Now ; num[Now] = num[fail[Now]] + 1 ; } last = Next[cur][c] ; cnt[last]++ ; return num[last] ; } int Count(){ ll res = 0 ; for (int i = p - 1; i > 0; i--){ // cnt[fail[i]] += cnt[i] ; cnt[fail[i]] = (cnt[fail[i]] + cnt[i]) % MOD ; res = (res + cnt[i]) % MOD ; } return res ; } }pt; char str[Maxn] ; int sum[Maxn] ; int main (){ while (~scanf("%d", &n)){ scanf("%s", str) ; ll ans = 0 ; pt.init() ; sum[n] = 0 ; for (int i = n - 1; i >= 0; i--) { sum[i] = (sum[i + 1] + pt.add(str[i])) % MOD ; } pt.init() ; for (int i = 0; i < n; i++){ ans = (ans + (ll) pt.add(str[i]) * sum[i + 1]) % MOD ; } ll all = pt.Count() ; ans = (((ll) all * (all - 1) / 2 % MOD - ans) % MOD + MOD) % MOD ; printf("%lld\n", ans) ; } return 0 ; } Manacher:
/* @Author: Top_Spirit @Language: C++ */ //#include
#include #include #include #include #include using namespace std ; typedef unsigned long long ull ; typedef long long ll ; const int Maxn = 2e6 + 10 ; const int INF = 0x3f3f3f3f ; const double PI = acos(-1.0) ; const ull seed = 133 ; const int MOD = 51123987 ; string s ; int n ; int len, l[Maxn << 1], r[Maxn << 1], ve[Maxn << 1] ; void Manacher (){ string str ; str = "$#" ; for (int i = 0; i < s.size(); i++){ str.append(s, i, 1); str.append("#") ; } str.append("@") ; int Mx = 0, id = 0, maxLen = 0, index = 0 ; for (int i = 1; i < str.size() - 1; i++){ ve[i] = Mx > i ? min(ve[2 * id - i], Mx - i) : 1 ; while (str[i + ve[i]] == str[i - ve[i]]) ve[i]++; if (Mx < i + ve[i]){ Mx = i + ve[i] ; id = i ; } } ll ans = 0, sum = 0 ; for (int i = 1; i < str.size() - 1; i++){ l[i - ve[i] + 1]++ ; //求出该点最长回文串后,这一段均为回文子串,都要将l[i]和r[i]更新 l[i + 1]-- ; r[i]++ ; r[i + ve[i]]-- ; (ans += ve[i] / 2) %= MOD ; } ans = ans * (ans - 1ll) / 2 % MOD ; for (int i = 1; i < str.size() - 1; i++){ l[i] += l[i - 1] ; r[i] += r[i - 1] ; if (i % 2 == 0) { (ans -= sum * l[i] % MOD) %= MOD ; (sum += r[i]) %= MOD ; } } cout << (ans + MOD) % MOD << endl ; } int main (){ cin >> n >> s ; Manacher() ; return 0 ; }