给一个1e5的字符串,选出尽量多的回文串(可以相交)使得他们两两互不为子串。
先建出pam,然后将fail边与转移视作有向边(fail为父亲指向儿子),然后可以发现包含一个回文串x的其他回文串都可以走到x的那个点
转化为dag求最大独立集。根据dilworth定理(好像叫这个?),最大反链 = 最小链覆盖(最大独立集 = 最小路径覆盖),于是即为求最小链覆盖。
拆出入点,初始有n条路径,每有一两两匹配即合并两条路径,于是最大独立集即为点数 - 最大匹配数。
主要是存一下pam的板子,0为偶根,1为奇根,fail[偶] = 1,len[0] = -1。这种写法不用加特判,非常舒服。
#include
#include
#include
using namespace std;
const int N = 2e5 + 100, inf = 1e9;
int t,n;
char s[N];
struct pamT{
int len[N], c[N][26], fail[N], tot, last;
void insert(int x) {
int r = s[x] - 'a';
while (s[x] != s[x - len[last] - 1])
last = fail[last];
if (!c[last][r]) {
++tot; len[tot] = len[last] + 2;
int k = fail[last];
while (s[x] != s[x - len[k] - 1]) k = fail[k];
fail[tot] = c[k][r];
c[last][r] = tot;
last = tot;
} else last = c[last][r];
}
void init() {
memset(c, 0, sizeof c); //0偶, 1奇
tot = 1, fail[0] = 1;
len[1] = -1;
last = 0;
}
} pam;
int tot,S,T;
const int E = 1e5 * 26 * 2;
int final[N * 2], to[E], nex[E], e[E], cnt;
void link0(int x,int y,int ee) {
to[++tot] = y, nex[tot] = final[x], final[x] = tot;
e[tot] = ee;
}
void link(int x,int y,int ee) {
link0(x, y, ee), link0(y, x, 0);
// cout<<x<<" "<<y<1;
memset(final,0,sizeof final);
cnt = pam.tot;
S = cnt * 2 + 1, T = S + 1;
for (int i = 2; i <= cnt; i++) {
link(S, i, 1);
link(i + cnt, T, 1);
if (pam.fail[i] > 1) {
link(pam.fail[i], cnt + i, 1);
}
for (int j = 0; j < 26; j++) if (pam.c[i][j]) {
link(i, cnt + pam.c[i][j], 1);
}
}
}
int gap[2 * N], label[2 * N];
int go(int x,int wait) {
if (x == T) return wait;
int used = 0;
for (int i = final[x]; i; i = nex[i]) if (e[i] && label[x] == label[to[i]] + 1) {
int take = go(to[i], min(e[i], wait - used));
used += take;
e[i] -= take, e[i ^ 1] += take;
if (used == wait) return wait;
}
if ((--gap[label[x]++]) == 0) label[0] = inf;
gap[label[x]] ++;
return used;
}
int maxflow() {
int ret = 0;
memset(gap, 0, sizeof gap); gap[0] = cnt * 2;
memset(label, 0, sizeof label);
while (label[0] != inf)
ret += go(S, inf);
return ret;
}
int main() {
freopen("pam.in","r",stdin);
for (cin>>t; t; t--) {
pam.init();
scanf("%s", s + 1);
n = strlen(s + 1);
for (int i = 1; i <= n; i++) pam.insert(i);
build();
printf("%d\n",cnt - 1 - maxflow());
}
}