目录

  • hdu6599题意:
  • manacher+后缀自动机+倍增 \(O(nlog(n))\)
  • manacher+后缀数组+二分 \(O(nlog(n))\)
  • 回文树(回文自动机) \(O(n)\)

@(hdu6599:求本质不同的回文串长度及数量)
欢迎交流

hdu6599题意:

传送门: here

求有多少个回文串的前\(⌈ \frac {len}{2} ⌉\)个字符也是回文串。(两组解可重复)
将这些回文串按长度分类,分别输出长度为\(1,2,...,n\)的合法串的数量。

前期分析:
首先考虑回文串怎么求?\(manacher\)可以求出以\(i\)点为端点或中心的回文串数量。
但是求出来后你没法判断他的前半部分是否也是回文串。
推荐一个南京\(manacher\)练习题:here
还有注意一个字符串的回文串的数量太多了!
就算你能快速判断一个回文串是否满足条件,你不能依次判断每个回文串是否可行呀!

有一个结论:

一个字符串本质不同的回文串的数量级是\(O(n)\)的。

然后只要能快速求出一个子串且是回文串在原串的出现次数本题就解决了。
有个类似的题是求子串的,方法可以后缀数组也可以后缀自动机:here
一看数据范围\(|S|\le 3e5\),然后尝试我就挣扎了好多\(O(nlog(n))\)的做法,当然也有\(O(n)\)的做法(哭戚戚qwq

manacher+后缀自动机+倍增 \(O(nlog(n))\)

manacher得到本质不同的回文串

  • 首先所有本质不同的回文串都会在\(manacher\)扩展的时候遍历到,然后\(manacher\)是均摊\(O(n)\)的复杂度。
  • 那现在我可以大致获得\(O(n)\)级别的回文串,我再\(hash\)去重一下就可以得到所有本质不同的回文串,顺便\(hash\)判断它是否合法。

去重之类的可以\(hash(取模或uLL),map,unordered\_map\),自信不会\(hash\)冲突的话可以用一个\(bool\)数组代替\(map\)。

统计子串在母串中出现次数

  • 这样接下来我只要能求出这个字符串\((l,r)\)在母串中出现的次数,本题就结束了。
  • 后缀自动机里:\(|endpos(u)|\)表示状态\(u\)这个集合内字符串出现的次数。
  • 我从字符串\((l,r)\)右端点字符所在的状态沿着后缀连接树网上跳,遇到一个状态的\(minlen(u)\)大于等于这个字符串的长度就停下来,这个子串就肯定在状态\(u\)集合里面,出现次数即\(|endpos(u)|\)。
  • 暴力跳肯定不行,利用\(lca\)的倍增算法预处理一下就行了,你可以在后缀连接树上搜一遍,也可以拓扑排序后\(for\)预处理一遍,也可以基数排序后\(for\)一下预处理一遍。

对了,如果内存比较紧张,可以不用另开数组来倍增,直接用后缀自动机的\(nex\)数组,但是,一定要注意初始化!!!

Code

#pragma comment(linker, "/STACK:102400000,102400000")
#include<bits/stdc++.h>
#define fi first
#define se second
#define endl '\n'
#define o2(x) (x)*(x)
#define BASE_MAX 30
#define mk make_pair
#define eb emplace_back
#define all(x) (x).begin(), (x).end()
#define clr(a, b) memset((a),(b),sizeof((a)))
#define iis std::ios::sync_with_stdio(false); cin.tie(0)
#define my_unique(x) sort(all(x)),x.erase(unique(all(x)),x.end())
using namespace std;
#pragma optimize("-O3")
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<int, int> pii;
inline LL read() {LL x = 0;int f = 0;char ch = getchar();while (ch < '0' || ch > '9') f |= (ch == '-'), ch = getchar();while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();return x = f ? -x : x;
}
inline void write(LL x, bool f) {if (x == 0) {putchar('0'); if(f)putchar('\n');else putchar(' ');return;}if (x < 0) {putchar('-');x = -x;}static char s[23];int l = 0;while (x != 0)s[l++] = x % 10 + 48, x /= 10;while (l)putchar(s[--l]);if(f)putchar('\n');else putchar(' ');
}
int lowbit(int x) { return x & (-x); }
template<class T>T big(const T &a1, const T &a2) { return a1 > a2 ? a1 : a2; }
template<typename T, typename ...R>T big(const T &f, const R &...r) { return big(f, big(r...)); }
template<class T>T sml(const T &a1, const T &a2) { return a1 < a2 ? a1 : a2; }
template<typename T, typename ...R>T sml(const T &f, const R &...r) { return sml(f, sml(r...)); }
void debug_out() { cerr << '\n'; }
template<typename T, typename ...R>void debug_out(const T &f, const R &...r) {cerr << f << " ";debug_out(r...);}
#define debug(...) cerr << "[" << #__VA_ARGS__ << "]: ", debug_out(__VA_ARGS__);#define print(x) write(x);const LL INFLL = 0x3f3f3f3f3f3f3f3fLL;
const int HMOD[] = {1000000009, 1004535809};
const LL BASE[] = {1572872831, 1971536491};
const int mod = 998244353;
const int MOD = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const int MXN = 3e5 + 7;
const uLL base = 19260817;
char s[MXN];
int p[MXN], bin[22];
LL ANS[MXN];
uLL hs[MXN], bs[MXN];
inline uLL get_hash(int l, int r) {return hs[r] - hs[l-1]*bs[r-l+1];
}
unordered_map<uLL, int> ump;
int n;
struct Suffix_Automaton {static const int maxn = 3e5 + 105;static const int MAXN = 3e5 + 5;//basic
//    map<char,int> nex[maxn * 2];int nex[maxn*2][26];int link[maxn * 2], len[maxn * 2];int last, cnt;LL tot_c;//不同串的个数//extensionint cntA[MAXN * 2], A[MAXN * 2];//辅助拓扑更新int nums[MAXN * 2];//每个节点代表的所有串的出现次数int dep[MAXN*2], pos[MAXN*2];void clear() {tot_c = 0;last = cnt = 1;link[1] = len[1] = 0;
//        nex[1].clear();memset(nex[1], 0, sizeof(nex[1]));for(int i = 0; i <= 2 * n + 1; ++i) nums[i] = 0;}void add(int c, int id) {int p = last, np = ++ cnt;
//        nex[cnt].clear();memset(nex[cnt], 0, sizeof(nex[cnt]));len[np] = len[p] + 1;nums[np] = 1;pos[id] = np;last = np;while (p && !nex[p][c])nex[p][c] = np, p = link[p];if (!p)link[np] = 1, tot_c += len[np] - len[link[np]];else {int q = nex[p][c];if (len[q] == len[p] + 1)link[np] = q, tot_c += len[np] - len[link[np]];else {int nq = ++cnt;len[nq] = len[p] + 1;
//                nex[nq] = nex[q];memcpy(nex[nq], nex[q], sizeof(nex[q]));link[nq] = link[q];link[np] = link[q] = nq;tot_c += len[np] - len[link[np]];while (nex[p][c] == q)nex[p][c] = nq, p = link[p];}}}void build(int n) {
//        memset(cntA, 0, sizeof cntA), memset(nums, 0, sizeof nums);for(int i = 0; i <= cnt; ++i) cntA[i] = 0;for (int i = 1; i <= cnt; i++)cntA[len[i]]++;for (int i = 1; i <= n; i++)cntA[i] += cntA[i - 1];for (int i = cnt; i >= 1; i--)A[cntA[len[i]]--] = i;//更行主串节点
//        int temps = 1;
//        for (int i = 1; i <= n; ++i) {
//            nums[temps = nex[temps][s[i] - 'a']] = 1;
//            debug(temps, s[i-1])
//        }//拓扑更新for (int i = cnt, x; i >= 1; i--) {//basicx = A[i];nums[link[x]] += nums[x];}
//        for(int i = 1; i <= cnt; ++i) debug(nums[i])for (int i = 1, t; i <= cnt; i++) {t = A[i];dep[t] = dep[link[t]] + 1;nex[t][0] = link[t];
//            for (int j = 1; bin[j] <= dep[t]; j++)for (int j = 1; j < 20; j++)if(bin[j] <= dep[t]) nex[t][j] = nex[nex[t][j - 1]][j - 1];else nex[t][j] = 0;}}
//    void DEBUG() {
//        for (int i = cnt; i >= 1; i--) {
//            printf("nums[%d]=%d numt[%d]=%d len[%d]=%d link[%d]=%d\n", i, nums[i], i, nums[i], i, len[i], i, link[i]);
//        }
//    }int query(int l, int r) {int mid = pos[r];for (int i = 19; i >= 0; i--) {int t = nex[mid][i];if (len[t] >= r - l + 1) mid = t;}
//        ans += siz[mid];ANS[r-l+1] += nums[mid];
//        debug(l, r, nums[mid])}
} sam;void manacher() {int mx = 0, id;for (int i = 1; i <= n; i++) {if (mx > i)p[i] = min(mx - i, p[2 * id - i]);else p[i] = 0;while (s[i + p[i] + 1] == s[i - p[i]]) {if(ump.find(get_hash(i - p[i], i + p[i] + 1)) == ump.end()) {ump[get_hash(i - p[i], i + p[i] + 1)] = 1;
//                debug(gethash(i - p[i], i + p[i] + 1), i - p[i], i + p[i] + 1)int l = i - p[i], r = i + p[i] + 1;if(get_hash(l, (l+r)/2) == get_hash((l+r)/2+1, r)) {sam.query(i - p[i], i + p[i] + 1);}}p[i]++;}if (p[i] + i > mx)mx = p[i] + i, id = i;}mx = 0;for (int i = 1; i <= n; i++) {if (mx > i)p[i] = min(mx - i - 1, p[2 * id - i]);else {p[i] = 1;if(ump.find(get_hash(i - p[i] + 1, i + p[i] - 1)) == ump.end()) {ump[get_hash(i - p[i] + 1, i + p[i] - 1)] = 1;
//                debug(gethash(i - p[i] + 1, i + p[i] - 1), i - p[i] + 1, i + p[i] - 1)int l = i - p[i] + 1, r = i + p[i] - 1;if(get_hash(l, (l+r)/2) == get_hash((l+r)/2, r)) {sam.query(i - p[i] + 1, i + p[i] - 1);}}}while (s[i + p[i]] == s[i - p[i]]) {if(ump.find(get_hash(i - p[i], i + p[i])) == ump.end()) {ump[get_hash(i - p[i], i + p[i])] = 1;
//                debug(gethash(i - p[i], i + p[i]), i - p[i], i + p[i])int l = i - p[i], r = i + p[i];if(get_hash(l, (l+r)/2) == get_hash((l+r)/2, r)) {sam.query(i - p[i], i + p[i]);}}p[i]++;}if (p[i] + i > mx)mx = p[i] + i, id = i;}
}
int main() {
#ifndef ONLINE_JUDGEfreopen("/home/cwolf9/CLionProjects/ccc/in.txt", "r", stdin);//freopen("/home/cwolf9/CLionProjects/ccc/out.txt", "w", stdout);
#endif
//    int tim = read();bin[0] = 1; bs[0] = 1;for (int i = 1; i < 20; i++) bin[i] = bin[i - 1] << 1;for(int i = 1;  i<= 300005; ++i) bs[i] = bs[i-1] * base;while (~scanf("%s", s + 1)) {
//        debug(s+1)n = strlen(s + 1);for(int i = 1; i <= n; ++i) hs[i] = hs[i-1] * base + s[i];sam.clear();ump.clear();for(int i = 0; i <= n + 2; ++i) p[i] = 0;for(int i = 1; i <= n; ++i) sam.add(s[i] - 'a', i), ANS[i] = 0;sam.build(n);manacher();for (int i = 1; i <= n; ++i) if (i == n) write(ANS[i], true); else write(ANS[i], false);}
#ifndef ONLINE_JUDGEcout << "time cost:" << clock() << "ms" << endl;
#endifreturn 0;
}

manacher+后缀数组+二分 \(O(nlog(n))\)

manacher得到本质不同的回文串

  • 首先所有本质不同的回文串都会在\(manacher\)扩展的时候遍历到,然后\(manacher\)是均摊\(O(n)\)的复杂度。
  • 那现在我可以大致获得\(O(n)\)级别的回文串,我再\(hash\)去重一下就可以得到所有本质不同的回文串,顺便\(hash\)判断它是否合法。

去重之类的可以\(hash(取模或uLL),map,unordered\_map\),自信不会\(hash\)冲突的话可以用一个\(bool\)数组代替\(map\)。

统计子串在母串中出现次数

  • 求两个后缀串的\(lcp\)就是直接\(height\)最小值,\(RMQ\)预处理一下就行。这是后缀数组的板子,大家都会。

  • 求一个子串出现次数,利用\(height\)数组,将排序后的后缀分组,把\(height\)大于等于\(len(l,r)\)的后缀分成一组,然后统计数量即可,这是国家集训队罗大佬论文里常规套路。
  • 对于分组后左右端点的确定,二分一下即可。(不过要注意细节

网上板子的\(RMQ\_Query(l,r)\)用着不习惯,自己改成自己习惯的写法,二分的时候注意一下细节即可。别忘了\(height[i]\)是\(sa[i-1]\)和\(sa[i]\)的\(lcp\)。

后缀数组写法可以参考一下这个:here

Code

#pragma comment(linker, "/STACK:102400000,102400000")
#include<bits/stdc++.h>
#define fi first
#define se second
#define endl '\n'
#define o2(x) (x)*(x)
#define BASE_MAX 30
#define mk make_pair
#define eb emplace_back
#define all(x) (x).begin(), (x).end()
#define clr(a, b) memset((a),(b),sizeof((a)))
#define iis std::ios::sync_with_stdio(false); cin.tie(0)
#define my_unique(x) sort(all(x)),x.erase(unique(all(x)),x.end())
using namespace std;
#pragma optimize("-O3")
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<int, int> pii;
inline LL read() {LL x = 0;int f = 0;char ch = getchar();while (ch < '0' || ch > '9') f |= (ch == '-'), ch = getchar();while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();return x = f ? -x : x;
}
inline void write(LL x, bool f) {if (x == 0) {putchar('0'); if(f)putchar('\n');else putchar(' ');return;}if (x < 0) {putchar('-');x = -x;}static char s[23];int l = 0;while (x != 0)s[l++] = x % 10 + 48, x /= 10;while (l)putchar(s[--l]);if(f)putchar('\n');else putchar(' ');
}
int lowbit(int x) { return x & (-x); }
template<class T>T big(const T &a1, const T &a2) { return a1 > a2 ? a1 : a2; }
template<typename T, typename ...R>T big(const T &f, const R &...r) { return big(f, big(r...)); }
template<class T>T sml(const T &a1, const T &a2) { return a1 < a2 ? a1 : a2; }
template<typename T, typename ...R>T sml(const T &f, const R &...r) { return sml(f, sml(r...)); }
void debug_out() { cerr << '\n'; }
template<typename T, typename ...R>void debug_out(const T &f, const R &...r) {cerr << f << " ";debug_out(r...);}
#define debug(...) cerr << "[" << #__VA_ARGS__ << "]: ", debug_out(__VA_ARGS__);#define print(x) write(x);const LL INFLL = 0x3f3f3f3f3f3f3f3fLL;
const int HMOD[] = {1000000009, 1004535809};
const LL BASE[] = {1572872831, 1971536491};
const int mod = 1e7 + 7;
const int MOD = 1e7 + 7;//998244353
const int INF = 0x3f3f3f3f;
const int MXN = 3e5 + 7;
const uLL base = 19260817;
char s[MXN];
int p[MXN], bin[22], lg2[MXN];
LL ANS[MXN];
uLL hs[MXN], bs[MXN];
unordered_map<uLL,bool> ump;
inline uLL get_hash(int l, int r) {return ((hs[r] - hs[l-1]*bs[r-l+1]));
}
int n;
//后缀数组(SA[i]存放排名第i大的后缀首字符的下标)
//名次数组(rank[i]存放Suffix(i)的优先级(名次))
//height数组:height[i]是Suffix(sa[i-1])和Suffix(sa[i])的最长公共前缀长度
//SA,R,H的下标都是 0~n 其中多包括了一个空字符串
struct Suffix_Array {static const int N = 3e5 + 7;int n, len, s[N], M;int sa[N], rnk[N], height[N];int tmp_one[N], tmp_two[N], c[N];int dp[N][21];void init_str(char *str, int _n) {len = _n;n = len + 1;for (int i = 0; i < len; ++i) s[i] = str[i];s[len] = '\0';}void build_sa(int m = 128);void calc_height(int n);void Out(char *str);void query(int l, int r);void RMQ_init(int n);int RMQ_query(int l, int r);
}sam;
void Suffix_Array::Out(char *str) {puts ("/*Suffix*/");for (int i=0; i<n; ++i) {printf ("%s\n", str+sa[i]);}
}
int Suffix_Array::RMQ_query(int l, int r) {//看自己需求自由变换int k = lg2[r - l + 1];
//    int k = 0; while (1<<(k+1) <= r - l + 1) k++;return min(dp[l][k], dp[r-(1<<k)+1][k]);
}
void Suffix_Array::RMQ_init(int n) {for (int i=0; i<n; ++i) dp[i][0] = height[i];for (int j=1; (1<<j)<=n; ++j) {for (int i=0; i+(1<<j)-1<n; ++i) {dp[i][j] = std::min (dp[i][j-1], dp[i+(1<<(j-1))][j-1]);}}
}
void Suffix_Array::calc_height(int n) {for (int i=0; i<=n; ++i) rnk[sa[i]] = i;int k = height[0] = 0;for (int i=0; i<n; ++i) {if (k) k--;int j = sa[rnk[i]-1];while (s[i+k] == s[j+k]) k++;height[rnk[i]] = k;}
}
//m = max(r[i]) + 1,一般字符128足够了
void Suffix_Array::build_sa(int m) {int i, j, p, *x = tmp_one, *y = tmp_two;for (i=0; i<m; ++i) c[i] = 0;for (i=0; i<n; ++i) c[x[i]=s[i]]++;//此时第一关键字是x[i],第二关键字是ifor (i=1; i<m; ++i) c[i] += c[i-1];for (i=n-1; i>=0; --i) sa[--c[x[i]]] = i;//排第几的后缀是ifor (j=1; j<=n; j<<=1) {//y就是第二关键字从小到大的位置//y[i]表示第二关键字排名为i的数,第一关键字的位置for (p=0, i=n-j; i<n; ++i) y[p++] = i;//这些数的第二关键字为0for (i=0; i<n; ++i) if (sa[i] >= j) y[p++] = sa[i] - j;//按rank顺序,1<<(j+1)的第二半的rank。sa[i]把自己交给了sa[i]-j//现在第二关键字已经有序,在此基础上按第一关键字排序for (i=0; i<m; ++i) c[i] = 0;for (i=0; i<n; ++i) c[x[y[i]]]++;for (i=1; i<m; ++i) c[i] += c[i-1];for (i=n-1; i>=0; --i) sa[--c[x[y[i]]]] = y[i];//排第几的后缀是y[i]std::swap (x, y);for (p=1, x[sa[0]]=0, i=1; i<n; ++i) {//排完序后更新第一关键字x[sa[i]] = (y[sa[i-1]] == y[sa[i]] && y[sa[i-1]+j] == y[sa[i]+j] ? p - 1 : p++);}if(p >= n) break;m=p;}calc_height(n-1);RMQ_init(n);
}
void Suffix_Array::query(int l, int r) {-- l, -- r;int len = r - l + 1, L = 1, R = rnk[l]-1, mid, ans = -1;while(L <= R) {mid = (L + R) >> 1;if(RMQ_query(mid+1,  rnk[l]) >= len) ans = mid, R = mid - 1;else L = mid + 1;}int tmp = ans;L =  rnk[l] + 1, R = n - 1, ans = -1;while(L <= R) {mid = (L + R) >> 1;if(RMQ_query( rnk[l] + 1, mid) >= len) ans = mid, L = mid + 1;else R = mid - 1;}if(ans == -1 && tmp == -1) ANS[r-l+1] ++;else if(ans == -1) ANS[r-l+1] +=  rnk[l] - tmp + 1;else if(tmp == -1) ANS[r-l+1] += ans - rnk[l] + 1;else ANS[r-l+1] += ans - tmp + 1;
//    printf("%d %d %c %d %d %d\n", l, r, s[l],  rnk[l], ans, tmp);
}
void manacher() {int mx = 0, id;for (int i = 1; i <= n; i++) {if (mx > i)p[i] = min(mx - i, p[2 * id - i]);else p[i] = 0;while (s[i + p[i] + 1] == s[i - p[i]]) {if(ump.find(get_hash(i - p[i], i + p[i] + 1)) == ump.end()) {ump[get_hash(i - p[i], i + p[i] + 1)] = true;int l = i - p[i], r = i + p[i] + 1;if(get_hash(l, (l+r)/2) == get_hash((l+r)/2+1, r)) {sam.query(i - p[i], i + p[i] + 1);}}p[i]++;}if (p[i] + i > mx)mx = p[i] + i, id = i;}mx = 0;for (int i = 1; i <= n; i++) {if (mx > i)p[i] = min(mx - i - 1, p[2 * id - i]);else {p[i] = 1;if(ump.find(get_hash(i - p[i] + 1, i + p[i] - 1)) == ump.end()) {ump[get_hash(i - p[i] + 1, i + p[i] - 1)] = true;int l = i - p[i] + 1, r = i + p[i] - 1;if(get_hash(l, (l+r)/2) == get_hash((l+r)/2, r)) {sam.query(i - p[i] + 1, i + p[i] - 1);}}}while (s[i + p[i]] == s[i - p[i]]) {if(ump.find(get_hash(i - p[i], i + p[i])) == ump.end()) {ump[get_hash(i - p[i], i + p[i])] = true;int l = i - p[i], r = i + p[i];if(get_hash(l, (l+r)/2) == get_hash((l+r)/2, r)) {sam.query(i - p[i], i + p[i]);}}p[i]++;}if (p[i] + i > mx)mx = p[i] + i, id = i;}
}
int main() {
#ifndef ONLINE_JUDGEfreopen("/home/cwolf9/CLionProjects/ccc/in.txt", "r", stdin);//freopen("/home/cwolf9/CLionProjects/ccc/out.txt", "w", stdout);
#endif
//    int tim = read();bin[0] = 1;for (int i = 1; i < 22; i++) bin[i] = bin[i - 1] << 1;for (int i = 2; i < MXN; ++i) lg2[i] = lg2[i >> 1] + 1;bs[0] = 1;for(int i = 1;  i< MXN; ++i) bs[i] = (bs[i-1] * base);while (~scanf("%s", s + 1)) {n = strlen(s + 1);sam.init_str(s + 1, n);sam.build_sa();
//        for(int i = 0; i <= n; ++i) printf("%d ", sam.sa[i]); printf("\n");for(int i = 1; i <= n; ++i) hs[i] = (hs[i-1] * base + s[i]);ump.clear();for(int i = 0; i <= n + 2; ++i) p[i] = 0;for(int i = 1; i <= n; ++i) ANS[i] = 0;manacher();for (int i = 1; i <= n; ++i) if (i == n) write(ANS[i], true); else write(ANS[i], false);}
#ifndef ONLINE_JUDGEcout << "time cost:" << clock() << "ms" << endl;
#endifreturn 0;
}

回文树(回文自动机) \(O(n)\)

回文树可以干啥?

假设我们有一个串\(S\),\(S\)下标从\(0\)开始,则回文树能做到如下几点:

1.求串\(S\)前缀\(0 - i\)内本质不同回文串的个数(两个串长度不同或者长度相同且至少有一个字符不同便是本质不同)
2.求串\(S\)内每一个本质不同回文串出现的次数
3.求串\(S\)内回文串的个数(其实就是1和2结合起来)
4.求以下标\(i\)结尾的回文串的个数

\(what??\)前面费劲力气干的事情,回文树\(O(n)\)全给你搞定。
本题我从这个博客找了板子,就是一个\(pos[]\)数组记录原字符串端点对应回文树上的端点,再用\(hash\)判断字符串是否相等。随便改改就\(ac\)了!??

简直自闭,出一个模板题!!醉了。。

code

#pragma comment(linker, "/STACK:102400000,102400000")
#include<bits/stdc++.h>
#define fi first
#define se second
#define endl '\n'
#define o2(x) (x)*(x)
#define BASE_MAX 30
#define mk make_pair
#define eb emplace_back
#define all(x) (x).begin(), (x).end()
#define clr(a, b) memset((a),(b),sizeof((a)))
#define iis std::ios::sync_with_stdio(false); cin.tie(0)
#define my_unique(x) sort(all(x)),x.erase(unique(all(x)),x.end())
using namespace std;
#pragma optimize("-O3")
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<int, int> pii;
inline LL read() {LL x = 0;int f = 0;char ch = getchar();while (ch < '0' || ch > '9') f |= (ch == '-'), ch = getchar();while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();return x = f ? -x : x;
}
inline void write(LL x, bool f) {if (x == 0) {putchar('0'); if(f)putchar('\n');else putchar(' ');return;}if (x < 0) {putchar('-');x = -x;}static char s[23];int l = 0;while (x != 0)s[l++] = x % 10 + 48, x /= 10;while (l)putchar(s[--l]);if(f)putchar('\n');else putchar(' ');
}
int lowbit(int x) { return x & (-x); }
template<class T>T big(const T &a1, const T &a2) { return a1 > a2 ? a1 : a2; }
template<typename T, typename ...R>T big(const T &f, const R &...r) { return big(f, big(r...)); }
template<class T>T sml(const T &a1, const T &a2) { return a1 < a2 ? a1 : a2; }
template<typename T, typename ...R>T sml(const T &f, const R &...r) { return sml(f, sml(r...)); }
void debug_out() { cerr << '\n'; }
template<typename T, typename ...R>void debug_out(const T &f, const R &...r) {cerr << f << " ";debug_out(r...);}
#define debug(...) cerr << "[" << #__VA_ARGS__ << "]: ", debug_out(__VA_ARGS__);#define print(x) write(x);const LL INFLL = 0x3f3f3f3f3f3f3f3fLL;
const int HMOD[] = {1000000009, 1004535809};
const LL BASE[] = {1572872831, 1971536491};
const int mod = 1e7 + 7;
const int MOD = 1e7 + 7;//998244353
const int INF = 0x3f3f3f3f;
const int MXN = 3e5 + 7;
const uLL base = 19260817;
char s[MXN];
int n;
LL ANS[MXN];
uLL hs[MXN], bs[MXN];
inline uLL get_hash(int l, int r) {return hs[r] - hs[l-1]*bs[r-l+1];
}
struct Palindromic_Tree {static const int MAXN = 600005 ;static const int CHAR_N = 26 ;int next[MAXN][CHAR_N];//next指针,next指针和字典树类似,指向的串为当前串两端加上同一个字符构成int fail[MAXN];//fail指针,失配后跳转到fail指针指向的节点int cnt[MAXN];int num[MAXN];int len[MAXN];//len[i]表示节点i表示的回文串的长度int S[MAXN];//存放添加的字符int last;//指向上一个字符所在的节点,方便下一次addint n;//字符数组指针int p;//节点指针int pos[MAXN];int newnode(int l) {//新建节点for (int i = 0; i < CHAR_N; ++i) next[p][i] = 0;cnt[p] = 0;num[p] = 0;len[p] = l;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) {//和KMP一样,失配后找一个尽量最长的while (S[n - len[x] - 1] != S[n]) x = fail[x];return x;}void add(int c, int id) {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];//和AC自动机一样建立fail指针,以便失配后跳转next[cur][c] = now;num[now] = num[fail[now]] + 1;}last = next[cur][c];cnt[last] ++;pos[last] = id;}void count() {for (int i = p - 1; i >= 0; --i) cnt[fail[i]] += cnt[i];//父亲累加儿子的cnt,因为如果fail[v]=u,则u一定是v的子回文串!for(int i = 0, tmp; i < p; ++i) {tmp = pos[i];if(len[i] % 2 == 0 && get_hash(tmp - len[i] + 1, tmp - len[i]/2) == get_hash(tmp - len[i]/2 + 1, tmp)) ANS[len[i]] += cnt[i];else if((len[i] & 1) && get_hash(tmp - len[i] + 1, tmp - len[i]/2) == get_hash(tmp - len[i]/2, tmp)) ANS[len[i]] += cnt[i];}}
} pt;
int main() {
#ifndef ONLINE_JUDGEfreopen("/home/cwolf9/CLionProjects/ccc/in.txt", "r", stdin);//freopen("/home/cwolf9/CLionProjects/ccc/out.txt", "w", stdout);
#endif
//    int tim = read();bs[0] = 1;for(int i = 1;  i< MXN; ++i) bs[i] = (bs[i-1] * base);while (~scanf("%s", s + 1)) {n = strlen(s + 1);for (int i = 1; i <= n; ++i) ANS[i] = 0, hs[i] = hs[i-1] * base + s[i];pt.init();for(int i = 1; i <= n; ++i) pt.add(s[i], i);pt.count();for (int i = 1; i <= n; ++i) if (i == n) write(ANS[i], true); else write(ANS[i], false);}
#ifndef ONLINE_JUDGEcout << "time cost:" << clock() << "ms" << endl;
#endifreturn 0;
}

转载于:https://www.cnblogs.com/Cwolf9/p/11253106.html

HDU6599:求本质不同的子串(回文串)长度及数量相关推荐

  1. 2019杭电多校第二场1009 HDU6599:求本质不同的回文串长度及数量

    hdu6599:求本质不同的回文串长度及数量 hdu6599题意: manacher+后缀自动机+倍增 $O(nlog(n))$ manacher+后缀数组+二分 $O(nlog(n))$ 回文树(回 ...

  2. 【132】求把字符串分割成回文串的最少切分次数

    给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是回文. 返回符合要求的 最少分割次数 . 示例 1: 输入:s = "aab" 输出:1 解释:只需一次分割就可将 s ...

  3. usaco Calf Flac(O(n)时间求回文串长度)

    好高兴,为数不多的我没看题解的题·,我用的是O(n)时间求回文串长度的算法算法在我上一篇博客. 然后就是注意细节了. /* ID:jinbo wu TASK: calfflac LANG:C++ */ ...

  4. leetcode 高薪_利用两种不同的方法解LeetCode第1312题:让字符串成为回文串的最少插入次数

    题目描述(难度困难) 给你一个字符串 s ,每一次操作你都可以在字符串的任意位置插入任意字符.请你返回让 s 成为回文串的 最少操作次数 . 「回文串」是正读和反读都相同的字符串. 示例 1: 输入: ...

  5. 字符串处理 —— 回文串相关 —— 求最长回文子串

    [暴力枚举] 求最长回文串最容易的方法就是暴力枚举,求出字符串的每一个子串,然后判断是不是回文,找到最长的那个回文串 求每一个子串的时间复杂度为 O(N^2),判断一个子串是不是回文时间复杂度为 O( ...

  6. 算法62---最长回文子序列长度(子串)、回文子序列总共个数(子串)【动态规划】...

    参考链接:https://www.cnblogs.com/AndyJee/p/4465696.html 一.题目:最长回文子序列长度 给定字符串,求它的最长回文子序列长度.回文子序列反转字符顺序后仍然 ...

  7. 最长回文 HDU - 3068(求最长回文串的长度【马拉车算法Manacher】)

    马拉车算法 Manacher's Algorithm 是用来查找一个字符串的最长回文子串的线性方法,由一个叫 Manacher 的人在 1975 年发明的,这个方法的最大贡献是在于将时间复杂度提升到了 ...

  8. 求最长回文串-从动态规划到马拉车之路(下)

    预备知识: (1)在一个数轴上有两点i和j(i<=j)关于点m对称,那么有 i = 2m-j: 证明: 因为 i<=j 且 i 和 j 关于 m 对称,那么有 (i + j)/ 2 = m ...

  9. P4445 最长回文串

    P4445 最长回文串 题目描述 顺序和逆序读起来完全一样的串叫做回文串.比如acbcaacbcaacbca是回文串,而abcabcabc不是(abc的顺序为abcabcabc,逆序为cbacbacb ...

最新文章

  1. UVa 11021 (概率 递推) Tribles
  2. 溢价 5 倍欲将 SiFive 收入麾下,英特尔的绝地反击战
  3. wxWidgets:持久对象概述
  4. c++ 特定容器算法(sort,merge,reverse,remove,unique)
  5. 数据分析如何从1进阶到10?
  6. transporter上传卡正在交付_【iOS】Xcode11使用Transporter将APP上传到App Store,卡在正在验证APP...
  7. 浅谈一下session问题
  8. 20.案例实战:为@Async实现一个自定义线程池
  9. tomcat依赖导入步骤_将tomcat源码导入eclipse
  10. java json序列化日期类型
  11. android获取浏览器cookie,获取浏览器cookie
  12. android 跳转京东app,第三方应用跳转到京东app
  13. springBoot17_缓存:环境搭建、原理、Cacheable、CachePut、CacheEvit、Caching、阿里云镜像加速、整合redis作为缓存、缓存原理、自定义缓存
  14. 十一黄金周旅游线路排行榜(图文)
  15. xkcd 单线程下载图片
  16. 打字游戏之主界面实现
  17. 基于SpringBoot+Vue的酒店管理系统(免费获取源码+项目介绍+运行介绍+运行截图+论文)
  18. 怀孕必读(非常实用)-怀胎的历程及注意事项
  19. SpringBoot整合Prometheus实现业务指标上报
  20. GPU/CPU性能天梯图

热门文章

  1. 关系模型的完整性规则。在参照完整性中,什么情况下外码属性的值可以为空值?
  2. realloc函_realloc函数使用规则
  3. Problem A: 让动物们叫起来吧!
  4. 九州云入选2021中国信创500强
  5. android 投屏代码,视频投屏,支持网络投屏和本地投屏
  6. 关于“定金”的法律规定及司法解释
  7. 区块链与数字货币的发展到底有什么意义
  8. python爬虫 慢慢买历史低价爬取 js逆向入门
  9. Pico VR一体机设置主launcher方案
  10. mulesoft Module 3 quiz 解析