滑动窗口之字符串重复不超过k

给一串字符串,分割字符串以后,字符串前缀和后缀拼接后组成str,要求str中重复字符的个数不能大于k。

如“abcab”,a|b|cab   str="acab",要求分割出去的字符串长度len, 1≤len≤n-1,也就是说最好删除一个,最多删除n-1个

输入n表示字符串长度,k 表示不得大于的重复数字

输入

5 1
abcab

输出:

7

|ab|cab 删除“ab” 有三种“cab”,“ab”,“b”

a|bca|b 删除“bca”,有两种“ab”,“a”

ab|cab| 删除“cab”,“ab”

abc|ab| "abc"

 

输入:

3 1
abc

输出:

5

输入:

5 2
abbab

输出:

12

输入:

48 5
cacabbabcabbabbcabbabcabbabbcabcabbabbacabbabbab

输出

89

 

题解:

假设前缀和后缀拼接为 newString

我们滑动窗口来解决这个问题,设置两个指针,分别代表两个“|”,对于右指针j的移动,会减少newString的字符个数,移动左指针i会增加newString的个数,因此有情况:

(1)newString不满足情况时,我们需要移动右指针j,删除一些重复个数>k的字符;

(2)当newString满足条件时,我们右指针当前位置即后继位置均满足条件,此时只需要计算后(n-j)的长度,(ps当左指针i在开头字符串最前面时,右指针不能移动到最后)

(3)承接(2),我们可以移动左指针i,如果增加一个字符,依旧满足条件k,则,右指针j,可以相继移动(n-j)为,进行枚举

(3)承接(2),我们可以移动左指针i,如果增加一个字符,不能满足条件k,则,需要返回(1)

 

 1 n, k = list(map(int, input().split()))
 2 str1 = input().strip()
 3 last = dict()
 4 for i in str1:
 5     if i not in last:
 6         last[i] = 1
 7     else:
 8         last[i] += 1
 9 lk = 0
10 for key,v in last.items():
11     if v>k:
12         lk += 1
13 
14 res = []
15 i, j = 0, 0
16 count= -1 #删除字串长度len, 1≤len≤n-1, 如果|ab|cab |abc|ab |abca|b 【|abcab|,不成立】
17 while j !=n:
18     for s in range(j, n):
19         last[str1[s]] -= 1
20         if last[str1[s]] == k:
21             lk -= 1
22         if lk == 0:
23             count+=(n-s)
24             break
25     j = s+1 #右指针下一时刻的位置
26     for t in range(i, j): #t∈[i,s]
27         last[str1[t]] += 1
28         if last[str1[t]] > k:
29             lk += 1
30             break
31         if t<s: #删除字串长度len, 1≤len≤n-1,
32             #如果加入的字符,没有破坏>k的协议,那么,已经满足的suffix,可以切开
33             #新加【c】 ab|cb|def   abc|b|def  abc|bd|ef abc|bde|f  abc|bdef|   
34             count+=(n-s)
35     i = t+1
36 print(count)

 

posted @ 2022-04-18 20:39  浅忆~  阅读(73)  评论(0编辑  收藏  举报