题目链接 Alternating Strings II
题意是指给出一个长度为n的01串,和一个整数k,要求将这个01串划分为很多子串(切很多刀),使得每个子串长度不超过k,且每个字串不是01交替出现的串(例如01, 10, 101, 010, 101010这些都是01交替出现的串),求最少需要切多少次
令F[i]代表前i个数所需要切的最少的刀数(从1开始计数),那么有
F[i] = min{F[j] | |j + 1, i| <= k 且 [j, i]这个子串不是01交替出现的串} + 1
这时O(n^2)的思路,足以解决上一个问题Gym 100712D了(n=1000),但是这个题数据n<=100000,所以首先寻求NlogN的方法。
我们记录以i结尾的且是交替串的最长长度为pre,可以看到在计算f[i]时,有以下几种情况:
1.s[i] == s[i - 1] 这时候没有以i结尾交替出现的串,重置pre=1,f[i] = min{f[i - k], ..., f[i - 1]} + 1 (初始化f[0] = -1,注意f下标是从1开始计数的)
2.s[i] != s[i - 1] 这时候pre++,由于最后一个与前一个形成交替串,所以需要比较pre与k的关系:
1) 若pre >= k 或 pre == i,着说明连续出现的交替串长度>k,或者前i个全是01串,那么此时f[i]只能由f[i - 1]转移(相当于在i-1与i之间切一刀)
2)否则的话pre + 1这个串([i - pre - 1, i])一定不是交替串,那么此时[i - k, i - pre - 1]这个区间的所有点都可以转移到i【1】(最后pre+1个不是交替串,那么最后pre+1+x个一定也不是),所以有f[i] = min{f[i - k], ..., f[i - pre - 1]} + 1,所以可以使用线段树查询区间最小值,复杂度NlogN
【1】可以由i转移到j表示在i的后面切一刀,[i + 1, j]这个区间是合法串(非交替串)
下面是代码:


1 //#pragma comment(linker, "/STACK:1677721600") 2 #include
同时,这道题还有O(N)的方法(想了太久才搞出= =!)
在上面的过程中讲到,如果i可以由j转移得到,那么一定可以由j - 1转移得到(前提是|j - 1, i| <= k), 那么如果此时f[j - 1] > f[j],那么在计算f[i]时吗,只要j存在,就不可能由j - 1转移(这时显而易见的),所以我们使用队列维护一个f值不降的序列,并记录下标。
在计算f[i]时,首先将队首与i的区间长度>k的点删掉,然后再根据上面讨论的几种情况判断是根据队首转移还是队尾转移,然后将f[i]入队,再根据f[i]的值维护队列的单调性。
1 //#pragma comment(linker, "/STACK:1677721600") 2 #include