迴文自動機
以爲尾節點的字符串維護
增加一個節點後
即爲在原有的所有真後綴迴文串()
增加一個迴文串更新(mark[num]
)
if (!tree[last][in[i] - 'a']) {
/*operation*/
mark[num] = mark[fail[num]] + 1;
//統計以該節點爲結尾的迴文串個數
}
//更新last
cnt[i]=mark[last];
迴文自動機(PAM)
Interesting
本質不同的字符串總數
即爲節點個數,拋去1
號節點
mark[i]=num-1
Palindromes and Super Abilities
CA Loves Palindromic
維護本質不同的字符串(信息來自於子樹)
對於一個本質不同的字符串
他的子樹都可以訪問他
標記所有字符串,統計他所有子樹信息即可
由於編號越大,層數約深,所以倒着遍歷即可
for (int i = 1; i <= n; i++) {
while (in[i - len[last] - 1] != in[i]) last = fail[last];
if (!tree[last][in[i] - 'a']) {
/*operation*/
}
last = tree[last][in[i] - 'a'];
mark[last]++; //標記信息
}
for (int i = num; i; i--) {
mark[fail[i]] += mark[i];
//統計子樹信息
}
P3649 迴文串
拉拉隊排練
維護本質不同的迴文串(信息來自於父親、祖先)
建立樹
利用,回溯維護迴文串祖先的信息
void build() {
memset(head, 0, sizeof(int) * (num + 1)); tot = 0;
for (int i = 2; i <= num; i++) AddEdge(fail[i], i);
//建樹,用fail邊建樹
}
int vis[maxn],res[maxn];
void dfs(int now) {
vis[len[now]]++;//維護祖先信息
/*operation 統計答案*/
for (int i = head[now]; i; i = edge[i].next)
dfs(edge[i].v); //dfs
vis[len[now]]--;//回溯
return;
}
HDU6599 I Love Palindrome String
Palindromeness
維護信息有前後兩端的迴文串
建立兩個迴文串
一個正着建,一個倒着建
對於i
節點維護i
節點正序迴文串信息和i+1
節點的倒序迴文串信息合併即可
struct PAM {
int tree[maxn][26];
int len[maxn], fail[maxn];
int last, num , mark[maxn];
void insert(char* in, int n) {
/*operation*/
}
}pre, suf;
int main() {
scanf("%s", in + 1); in[0] = '#';
int n = strlen(in + 1);
pre.insert(in, n);
//正向建立
reverse(in + 1, in + 1 + n);
suf.insert(in, n);
//反向建立
int ans = 0;
for (int i = 1; i < n; i++) {
ans = max(ans, pre.mark[i] + suf.mark[n-i]);
//兩個串信息合併
}
}
P4287 雙倍迴文
Harry and magic string
前端添加節點
pre,last
兩個標記維護前後添加
以pre
爲例,當添加節點後
新增的節點後最長迴文串不是本身時,新添加的節點不會影響last
即爲正確的
新增的節點後,本身爲最長迴文串時,要更新last
更新方法爲將
由於只統計一邊,所以統計不同本質字符串個數和總共字符串個數是正確的
但維護其他信息的時候需要認真思考
以下爲向前插入:
向後插入類似
void pre_insert(char* in, int i) { //向前添加方法
while (in[i + len[pre] + 1] != in[i]) pre = fail[pre];
if (!tree[pre][in[i] - 'a']) {
len[++num] = len[pre] + 2;
int j = fail[pre];
while (in[i + len[j] + 1] != in[i]) j = fail[j];
fail[num] = tree[j][in[i] - 'a'];
tree[pre][in[i] - 'a'] = num;
mark[num] = mark[fail[num]] + 1; //統計個數
}
pre = tree[pre][in[i] - 'a'];
if (len[pre] == stdr - stdl + 1) last = pre;//當本身變爲迴文串,更新last
sum = sum + mark[pre]; //維護總個數
}
Victor and String
前向星優化空間
當MLE
時,我們可以使用鏈式前向星優化
即使用鏈式前向星創建字典樹
查詢:遍歷所有邊,由於每個點在字典樹訪問次數有限,所以複雜不會超
賦值:即爲建邊
struct Edge{
int v;
int w; //w爲字母的值
int next;
}edge[maxn];
int head[maxn], tot;
inline void AddEdge(int u, int v, int w) {
edge[++tot].v = v;
edge[tot].w = w;
edge[tot].next = head[u];
head[u] = tot;
}
int find(int now, int w) { //尋找函數
for (int i = head[now]; i; i = edge[i].next)
if (edge[i].w == w) return edge[i].v;
return 0;
}
for (int i = 1; i <= n; i++) {
while (in[i - len[last] - 1] != in[i]) last = fail[last];
int treenode = find(last, in[i] - 'a');//尋找節點
if (!treenode) {
len[++num] = len[last] + 2;
int j = fail[last];
while (in[i - len[j] - 1] != in[i]) j = fail[j];
fail[num] = find(j, in[i] - 'a');//尋找節點
AddEdge(last, num, in[i] - 'a');//建邊
mark[num] = mark[fail[num]] + 1;
treenode = num; //更新節點
}
last = treenode;
cnt[i] = mark[last];
}