1
0

00-不从头再看的字符串.py 3.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. #!/usr/bin/env python3
  2. """课前零食:追踪 KMP 的 border 与 Manacher 的镜像半径。"""
  3. from __future__ import annotations
  4. def ask(prompt: str, default: str) -> str:
  5. try:
  6. return input(prompt).strip() or default
  7. except EOFError:
  8. return default
  9. def prefix_function(pattern: str) -> list[int]:
  10. pi = [0] * len(pattern)
  11. j = 0
  12. for i in range(1, len(pattern)):
  13. while j and pattern[i] != pattern[j]:
  14. print(f"i={i},{pattern[i]} != {pattern[j]}:j 从 {j} 跳到 pi[{j - 1}]={pi[j - 1]}")
  15. j = pi[j - 1]
  16. if pattern[i] == pattern[j]:
  17. j += 1
  18. pi[i] = j
  19. print(f"pi[{i}]={j}({pattern[:i + 1]!r} 的最长 border)")
  20. return pi
  21. def naive_count(text: str, pattern: str) -> int:
  22. comparisons = 0
  23. for start in range(len(text) - len(pattern) + 1):
  24. for j, char in enumerate(pattern):
  25. comparisons += 1
  26. if text[start + j] != char:
  27. break
  28. return comparisons
  29. def kmp_lab() -> None:
  30. pattern = ask("模式串(回车为 ABABAC): ", "ABABAC")
  31. text = ask("文本串(回车为 ABABABACABA): ", "ABABABACABA")
  32. if not pattern:
  33. print("空模式串没有可演示的失配;请下次输入至少一个字符。")
  34. return
  35. print("\n--- 构建前缀函数 ---")
  36. pi = prefix_function(pattern)
  37. print("pi =", pi)
  38. print("\n--- 扫描文本 ---")
  39. j = 0
  40. matches: list[int] = []
  41. for i, char in enumerate(text):
  42. while j and char != pattern[j]:
  43. print(f"文本[{i}]={char} 失配:j {j} -> {pi[j - 1]}")
  44. j = pi[j - 1]
  45. if char == pattern[j]:
  46. j += 1
  47. if j == len(pattern):
  48. matches.append(i - len(pattern) + 1)
  49. print(f"在位置 {matches[-1]} 匹配成功;保留 border,j -> {pi[j - 1]}")
  50. j = pi[j - 1]
  51. print("匹配位置:", matches or "没有")
  52. print(f"朴素法会做 {naive_count(text, pattern)} 次字符比较(KMP 只维护一条 border 链)。")
  53. def manacher_lab() -> None:
  54. text = ask("字符串(回车为 abacaba): ", "abacaba")
  55. if not text:
  56. print("空串的回文半径没有戏份。")
  57. return
  58. radius = [0] * len(text)
  59. left, right = 0, -1
  60. print("\n--- 奇长度回文半径(含中心) ---")
  61. for center in range(len(text)):
  62. radius[center] = 1 if center > right else min(radius[left + right - center], right - center + 1)
  63. source = "从 1 开始" if center > right else f"借镜像 {left + right - center}"
  64. while center - radius[center] >= 0 and center + radius[center] < len(text) and text[center - radius[center]] == text[center + radius[center]]:
  65. radius[center] += 1
  66. if center + radius[center] - 1 > right:
  67. left = center - radius[center] + 1
  68. right = center + radius[center] - 1
  69. covered = text[center - radius[center] + 1:center + radius[center]]
  70. print(f"中心 {center}={text[center]!r}:{source},半径 {radius[center]},覆盖 {covered!r},当前 [L,R]=[{left},{right}]")
  71. print("半径数组:", radius)
  72. def main() -> None:
  73. choice = ask("字符串实验室:k=KMP,m=Manacher(回车为 k): ", "k").lower()
  74. if choice == "m":
  75. manacher_lab()
  76. else:
  77. kmp_lab()
  78. if __name__ == "__main__":
  79. main()