【代码超详解】UVA 129 Krypton Factor

这篇博客介绍了如何解决UVA 129 Krypton Factor问题,即如何生成字典序第n个不包含连续相同子串的困难序列。文章通过回溯法进行算法分析,提供了详细的代码实现,并解释了如何验证序列是否符合条件。
摘要由CSDN通过智能技术生成

一、题目描述
你的任务是编写一个程序判断一个序列是“容易序列”还是“困难序列”。定义:一个字符序列中如果含有两个相邻的完全相同的子序列,就说这个字符序列是“容易序列”。比如:
ABACBCBAD
BB
ABCDACABCAB
ABCDABCD
重复出现的相邻子串分别为:CB、B、CAB、ABCD。
以下例子都是“困难序列”:
D
DC
ABDAB
CBABCBA
输入包含两个整数 n 和 L ,其中 1 ≤ n ≤ 80 (uDebug 上这一题的输入中如果 n 超过 80 会拒绝输出,判定为无效输入),1 ≤ L ≤ 26 。输出用前 L 个大写字母构成的第 n 个“困难序列”(按字典序排序)。每 4 个字符一组,每两组用一个空格隔开。当输出 64 个字符以后还需要输出新字符时,需要换行。字符总数不超过 80 。

二、算法分析说明与代码编写指导
本文的算法分析与代码是根据 https://blog.csdn.net/iboxty/article/details/46136089 改编的。
我们同样采用回溯法,一个一个字符去构造要求的“困难序列”。
由于只需要输出第 n 个字典序的“困难序列”,因此我们额外设置一个 bool 变量 found 来记录是否已找到第 n 个解。如果是,就要退出全部的递归调用。主算法依然类似 DFS 。
设置一个变量 count 代表已经构造的解的编号。当 count == n 时递归终止,输出结果,结束全部递归。
输入 n 和 L 以后,调用 gen(0) 来完成本组输出。gen 函数传入的参数是当前正在构造的字符的下标 h
对每个大写字母 A 到 Z ,先尝试填入,然后记 ok 代表该解有效(初始状态)。
接下来就要开始验证已经构造的字符串是否存在连续两个相邻的相同子串。我们令 j = 1, 2, …, (h + 1) / 2 。在 j 改变的时候,就记 equal = true ,代表存在两个相邻的相同子串。记构造的字符串为 c 。
可以看出,j 值改变,相当于在已经构造的字符串上作一个标记,这个标记从倒数第二个字符开始逐渐往前移动。当 k 改变时,c[h - k] 与 c[h - k - j] 就分别从 j 标记右侧的第一个字符与 j 标记本身指向的字符开始逐渐左移,直到 c[h - k] 移动到 j 右侧的第一个字符时,本轮校验结束。这样在 j 从 1 最终移到字符串中间时,就分别验证了在从右往左的顺序下是否存在长度为 j 的连续相同的子串(如果 h + 1 为奇数,那么第一个字符 c[0] 不会被验证。当然此时这个字符也不用验证)。
如果在 j 变化的过程中,某个 j 值满足 c[h - k] = c[h - k - j] ,其中 k = 0,1,……,j - 1 ,就意味着该字符串存在相邻的两个相同子串,equal 为 true 不变。每遍历完一次 k 之后,如果 equal 仍为 true ,这个字串就不是我们要找的“困难序列”。记 ok = false ,不继续构造下一个字符,而是跳到循环开头 for (char i = 65; i < t; ++i) 将当前位置 h 填入下一个字母再验证(其中 t = L + 65)。否则,该字符串为符合条件的序列,解的数量 count 要加 1 ,同时构造下一个字符。
为什么不用验证中间是否存在或者从左往右又进行验证呢?因为当 h = 0,1,2,……时,在 h 取得某一个值 h0 (h0 = 1,2,3,……)时,就意味着在 h = h0 - 1 时的全部字符已经验证符合条件:
当 h = 0 时,因为不存在两个相邻的子串,所以肯定符合,此时这个“困难序列”为“A”。
h = 1 时,如果能通过验证(ok = true),就意味着 c[0] ≠ c[1] ,同样符合条件。
h = 2 时,如果能通过验证,就意味着 c[1] ≠ c[2] ,而 c[0] ≠ c[1] 已经验证,符合;
h = 3 时,如果能通过验证,就意味着 c[2] ≠ c[3] ,且 c[1] ≠ c[3] ,c[0] ≠ c[2],而 c[0] ≠ c[1] 、 c[1] ≠ c[2] 已经验证,符合;
……
所以在每层递归中只从右侧开始依次验证连续两段长度为 1 、2 、…… 、(h + 1) / 2

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值