显然只需要考虑每次加入一个新点的答案。
把加点之后不再是 border 的 border 删掉,把仍然是border的用 \(w[i]\) 更新一下答案(取个min)。
每次最多加入一个border,每个border最多会被删除一次。所以是\(O(n)\)的。
而每次用map暴力更新答案的话,均摊\(O(log_n)\)
以上内容默认看过题解。
时间复杂度瓶颈在于如何找到不合法的。
多数题解都说:“用kmp的失配指针构建一棵树” 或 “画出kmp的fail树”。看不懂。
附一张fail树的配图:
我们定义一个位置 \(i\) 的颜色为 \(s[nxt[i] + 1]\)
首先插入 \(i\) 之前的 border 都是针对 \(i-1\) 合法的 border,所以都是在 \(i-1\) 的 fail 树上的。
考虑暴力 : 从 \(i-1\)开始暴力跳nxt,看 fail 树上每个位置是否合法(颜色是否等于\(s[i]\))。
优化一下,一共有\(26\)种颜色(\(26\)个字母),而我们只留下一种(\(s[i]\)),维护一个数组\(lst[i][j]\),表示在 \(i\) 所处的 fail 树上,在 \(i\) 之前第一个颜色为 \(j\) 的位置,然后.......就硬跳。
点击查看代码
for(int j = 0 ; j < 26 ; j++)lst[i][j] = lst[nxt[i]][j];
lst[i][s[nxt[i]+1]-'a'] = nxt[i];
for(int j = 0 ; j < 26 ; j++)
{
if(j + 'a' == s[i])continue;
for(ll k = lst[i-1][j] ; k ; k = lst[k][j])
{
ll val = query(1,1,n,i-k,i-1);
mp[val]--;
ans -= val;
}
}