【题解】ABC237Ex - Hakata
我们将每个回文串作为一个节点,如果 \(T\) 是 \(S\) 的子串,就连一条有向边 \(S\to T\),最后我们可以得到一个 DAG。
然后我们要选出最多的节点使得不存在一个节点能到达另一个节点。
首先我们直到节点数是 \(<2|S|\) 的,因为本质不同的回文串只会在 manacher 算法向又扩展时出现,而扩展最多 \(N\) 次。
其次考虑到 DAG 上并不方便 DP,考虑图论模型。我们直到有最大流最小割定理,这里求最大点集,可以联想到求 DAG 的最小链覆盖。根据 Dilworth 定理,最大反链元素个数等于最小链覆盖数。所以我们直接拆点二分图匹配即可。
#define N 205
#define M 405
int n, hs[N][N], v[N][N]; char a[N];
bool ck(int l,int r){
int w = (r - l + 1) / 2;
rp(i, w)if(a[l + i - 1] != a[r - i + 1])return false;
return true;
}
map<int,int>c; map<Pr,int>o;
struct edge{int to, nxt, cap;}e[N * N];
int h[M], d[M], cur[M], tot = 1, s, t, idx;
queue<int>q;
void add(int x,int y,int z){
e[++tot].nxt = h[x], h[x] = tot, e[tot].to = y, e[tot].cap = z;
e[++tot].nxt = h[y], h[y] = tot, e[tot].to = x, e[tot].cap = 0;
}
bool bfs(){
rp(i, t)cur[i] = h[i], d[i] = 0;
while(!q.empty())q.pop();
q.push(s), d[s] = 1;
while(!q.empty()){
int x = q.front(); q.pop();
if(x == t)return true;
for(int i = h[x]; i; i = e[i].nxt)if(e[i].cap && !d[e[i].to])
d[e[i].to] = d[x] + 1, q.push(e[i].to);
}
return false;
}
int dfs(int x,int flow){
if(x == t)return flow;
int res = flow;
for(int &i = cur[x]; i; i = e[i].nxt)if(e[i].cap && d[e[i].to] == d[x] + 1){
int w = dfs(e[i].to, min(flow, e[i].cap));
if(!w)d[e[i].to] = 0;
else e[i].cap -= w, e[i ^ 1].cap += w, res -= w;
if(!res)return flow;
}
return flow - res;
}
int main() {
scanf("%s", a + 1);
n = strlen(a + 1);
rp(i, n)
rep(j, i, n)v[i][j] = ck(i, j), hs[i][j] = (hs[i][j - 1] * 1LL * bas + a[j]) % P;
rp(i, n)rep(j, i, n)if(v[i][j] && !c.count(hs[i][j]))c[hs[i][j]] = ++idx;
rp(i, n)rep(j, i, n)rep(x, i, j)rep(y, x, j)if((i != x || y != j) && v[i][j] && v[x][y] && !o[mp(hs[i][j], hs[x][y])])
add(c[hs[i][j]], idx + c[hs[x][y]], 1), o[mp(hs[i][j], hs[x][y])] = 1;
s = idx + idx + 1, t = s + 1;
rp(i, idx)add(s, i, 1), add(i + idx, t, 1);
int ans = idx;
while(bfs())ans -= dfs(s, idx);
cout << ans << endl;
return 0;
}