小M有一个由0和1组成的字符串。他可以进行一些操作来修改字符串,并且有一个目标:将字符串的价值最小化。所谓字符串的价值,是指通过多次操作删除相邻的相同字符后剩下的字符长度。 小M每次操作可以选择修改其中的一个字符,'0'可以变为'1','1'可以变为'0'。他可以进行恰好k次修改,目的是让最终得到的字符串价值尽可能小。 你能帮助小M计算通过k次操作后,字符串的最小价值吗?
-
处理特殊情况:当
k为 0 时:-
当
k等于 0 时,意味着不能对字符串进行修改操作,直接按照规则计算字符串的价值即可。 -
创建一个空列表
stack,用于模拟栈结构来处理相邻相同字符的删除操作。通过一个循环遍历输入字符串s的每个字符,对于每个字符char:- 如果
stack不为空且栈顶元素(通过stack[-1]获取)与当前字符char相等,说明出现了相邻的相同字符,按照规则将栈顶元素弹出(通过stack.pop()操作),相当于删除了这对相邻的相同字符。 - 如果
stack为空或者栈顶元素与当前字符不相等,将当前字符char添加到stack中(通过stack.append(char)操作)。
- 如果
-
循环结束后,
stack中剩下的字符就是经过相邻相同字符删除操作后的结果,返回stack的长度len(stack),它就是此时字符串的价值。
-
-
处理
k大于 0 的一般情况:-
初始化变量
min_value为n,用于记录通过k次操作后字符串能达到的最小价值,初始化为n是因为字符串长度为n,价值最大就是n,后续会不断更新这个变量来找到最小值。 -
通过一个循环遍历所有
2 ** n种可能的修改状态(通过state从 0 到1 << n - 1进行遍历,利用位运算来表示不同的修改情况,每一位对应字符串中的一个字符位置,为 1 表示修改该位置的字符,为 0 表示不修改)。对于每一种状态state,进行以下操作:-
首先构建一个临时字符串
cur_s,用于表示经过当前状态修改后的字符串。初始化一个变量change_count为 0,用于记录当前状态下已经进行的修改次数。通过一个内层循环遍历字符串s的n个字符(索引i从 0 到n - 1),对于每个位置i:- 如果
state的二进制表示中第i位为 1(通过(state >> i) & 1判断),说明要对该位置的字符进行修改,根据原字符是'0'还是'1',将其修改为相反的字符(cur_s += "1" if s[i] == "0" else "0"),同时将修改次数change_count加 1。 - 如果
state的二进制表示中第i位为 0,直接将原字符串中的该字符添加到cur_s中(cur_s += s[i])。 - 在每次修改次数加 1 后,检查
change_count是否大于k,如果大于k,说明当前修改状态不符合恰好进行k次修改的要求,直接跳出内层循环,不再继续构建当前状态下的字符串。
- 如果
-
如果经过内层循环后,修改次数
change_count恰好等于k,说明当前状态是符合要求的一种修改情况,此时按照与k = 0时类似的方法来计算该字符串cur_s的价值:创建一个空列表stack,通过一个循环遍历cur_s的每个字符,对于出现相邻相同字符的情况进行删除操作(通过判断栈顶元素与当前字符是否相等来决定是否弹出栈顶元素),最后得到经过处理后的stack,其长度len(stack)就是当前字符串cur_s的价值,将这个价值与当前记录的最小价值min_value进行比较,使用min函数取两者中的较小值来更新min_value,这样不断遍历所有可能的修改状态并比较更新,就能找到经过k次修改后字符串的最小价值。
-
-
-
返回最终结果:
-
当循环遍历完所有可能的修改状态(对于
k大于 0 的情况)或者处理完k = 0的特殊情况后,min_value变量中存储的就是通过k次操作后字符串的最小价值,将其返回即可。
-
时间复杂度方面,当 k = 0 时,遍历字符串一次来处理相邻相同字符删除的时间复杂度为 (n 为字符串长度)。当 k 大于 0 时,需要遍历所有 2 ** n 种可能的修改状态,对于每种状态构建字符串以及计算价值的操作时间复杂度大致为 (遍历字符串进行修改和处理相邻相同字符删除),所以整体时间复杂度为 ,在处理常规长度的字符串和较小的 k 值时能够通过穷举所有可能情况来计算出最小价值,但随着 n 的增大,计算量会呈指数级增长。