I Love Palindrome String
题意
给出字符串str
求出str
中的子串数
且该子串为回文串并且该子串的前半串(包括中间字符)也为回文串
思路
-
首先可以轻松预处理出所有本质不同回文串个数
即使用回文自动机+常规操作
-
将
fail[]
指针建树 -
在
fail
树上DFS
,在同一路径上的回文串为同一后缀的回文串DFS
回溯维护不同长度回文串个数 -
对于一个串,查询KaTeX parse error: Expected 'EOF', got '&' at position 28: …]>>1)+(len[now]&̲1)]是否存在即可
代码
回文自动机
void insert(char* in, int n) {
len[0] = 0;len[1] = -1; //回文自动机初始化
fail[0] = 1;fail[1] = 1;
num = 1;
last = 0;
for (int i = 1; i <= n; i++) { //插入后端节点
while (in[i - len[last] - 1] != in[i]) last = fail[last];
if (!tree[last][in[i] - 'a']) {
len[++num] = len[last] + 2;
int j = fail[last];
while (in[i - len[j] - 1] != in[i]) j = fail[j];
fail[num] = tree[j][in[i] - 'a'];
tree[last][in[i] - 'a'] = num;
mark[num] = 0;
}
last = tree[last][in[i] - 'a'];
mark[last]++; //更新本质不同回文串个数
}
for (int i = num; i >= 0; i--) //维护子树大小,即为该回文串个数
mark[fail[i]] += mark[i];
}
建fail
树
struct Edge { //链式前向星
int v;
int next;
} edge[maxn];
int head[maxn], tot;
void AddEdge(int u, int v) {
edge[++tot].v = v;
edge[tot].next = head[u];
head[u] = tot;
}
void build() { //建树
memset(head, 0, sizeof(int) * (num + 1));
tot = 0;
for (int i = 2; i <= num; i++) AddEdge(fail[i], i);
}
DFS
void dfs(int now) {
vis[len[now]]++; //标记已存在长度
if (vis[(len[now] + 1) / 2] > 0) res[len[now]] += mark[now];
//查询是否存在前缀为回文串
for (int i = head[now]; i; i = edge[i].next)
dfs(edge[i].v);
vis[len[now]]--; //回溯
return;
}
void slove(int n) {
memset(vis, 0, sizeof(int) * (n + 1));//初始化
memset(res, 0, sizeof(int) * (n + 1));
dfs(0);//dfs
for (int i = 1; i < n; i++) printf("%d ", res[i]);
printf("%d\n", res[n]);
}
AC
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 300010;
char in[maxn];
int g;
int tree[maxn][26];
int len[maxn], fail[maxn];
int mark[maxn];
int last, num;
void init() {
memset(tree, 0, sizeof(tree));
}
void insert(char* in, int n) {
len[0] = 0;
len[1] = -1;
fail[0] = 1;
fail[1] = 1;
num = 1;
last = 0;
for (int i = 1; i <= n; i++) {
while (in[i - len[last] - 1] != in[i]) last = fail[last];
if (!tree[last][in[i] - 'a']) {
len[++num] = len[last] + 2;
int j = fail[last];
while (in[i - len[j] - 1] != in[i]) j = fail[j];
fail[num] = tree[j][in[i] - 'a'];
tree[last][in[i] - 'a'] = num;
mark[num] = 0;
}
last = tree[last][in[i] - 'a'];
mark[last]++;
}
for (int i = num; i >= 0; i--)
mark[fail[i]] += mark[i];
}
struct Edge {
int v;
int next;
} edge[maxn];
int head[maxn], tot;
void AddEdge(int u, int v) {
edge[++tot].v = v;
edge[tot].next = head[u];
head[u] = tot;
}
void build() {
memset(head, 0, sizeof(int) * (num + 1));
tot = 0;
for (int i = 2; i <= num; i++) AddEdge(fail[i], i);
}
int vis[maxn];
int res[maxn];
void dfs(int now) {
vis[len[now]]++;
if (vis[(len[now] + 1) / 2] > 0) res[len[now]] += mark[now];
for (int i = head[now]; i; i = edge[i].next)
dfs(edge[i].v);
vis[len[now]]--;
return;
}
void slove(int n) {
memset(vis, 0, sizeof(int) * (n + 1));
memset(res, 0, sizeof(int) * (n + 1));
dfs(0);
for (int i = 1; i < n; i++) printf("%d ", res[i]);
printf("%d\n", res[n]);
}
int main() {
in[0] = '#';
while (~scanf("%s", in + 1)) {
init();
int n = strlen(in + 1);
insert(in, n);
build();
slove(n);
}
}