CF1286E

显然只需要考虑每次加入一个新点的答案。
把加点之后不再是 border 的 border 删掉,把仍然是border的用 \(w[i]\) 更新一下答案(取个min)。
每次最多加入一个border,每个border最多会被删除一次。所以是\(O(n)\)的。
而每次用map暴力更新答案的话,均摊\(O(log_n)\)


以上内容默认看过题解。
时间复杂度瓶颈在于如何找到不合法的。
多数题解都说:“用kmp的失配指针构建一棵树” 或 “画出kmp的fail树”。看不懂。

附一张fail树的配图:
image
我们定义一个位置 \(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\) 的位置,然后.......就硬跳。

upd:如果暴力跳是可能被卡成 \(O(n^2)\) 的,而维护了颜色后,每次跳到的border都是要删除的,而每个border只会被删除一次,所以均摊 \(O(1)\)

点击查看代码
	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;
            }
        }
posted @ 2022-11-09 12:05  5_Lei  阅读(53)  评论(0)    收藏  举报