Advertisement
goodwish

594. strStr II

Dec 1st, 2019
190
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 4.08 KB | None | 0 0
  1. way 2, KMP, suffix prefix. 利用重合前后缀的匹配原理,降低查找次数.
  2.  
  3. 数据结构:
  4. suffix array: 简称 A.
  5. source string: 简称 s.
  6. pattern / target string: 简称 p.
  7. size: 前后缀重合长度.
  8.  
  9. . 准备 suffix array,
  10. 标记和前缀相同的后缀,记录每个 位置 i 结尾的后缀与前缀的重复字符的长度.
  11. . 利用 suffix array, 省略不可能匹配的查找,
  12. 遇到不匹配时,只用从 已经匹配子串 与后缀相同的前缀尾部开始匹配查找.
  13. 如果没有 前后缀相同部分, pattern 从零开始.
  14.  
  15. s = "a.a.^^a.a.b-a.a.a.a.bxa__bb"
  16. p = "a.a.b-a.a.a.a.bxa"
  17.  
  18. a .a .^^a.a.b-a.a.a.a.bxa__bb
  19. 012345..
  20.  
  21. a.a.^ 与 a.a.-, a.a. 相同, ^ - 不同.
  22. 基于已经匹配的子串 a.a. 的重合前后缀, 是 a. ,
  23. 在 i 前一个字符结尾, 它的后缀与前缀相同部分是 a., 前后缀重合长度 = 2,
  24. j = 2, 即第三个位置.
  25. 我们可以从第三个位置开始新一轮匹配, 即 source index = 4, pattern index = 2.
  26. a.? == a.?, source v.s. pattern.
  27. 我们就不需要从 source index = 1 ( 0 的下一个) 开始从头匹配 pattern,
  28.  - 根据 suffix array, 我们知道前面没有重合部分.
  29. 也不需要从 pattern index = 0 ( source index = 2) 开始匹配 pattern,
  30.  - 因为已知前两个字符(a.)是前后缀重合. 只需要看看下一个字符是否相同.
  31.  
  32. 封装, build_suffix_array(p): 返回数组, 元素值是每个位置后缀与前缀重复的长度.
  33. 构造方法和字符串查找方法类似.
  34.  
  35. 初始化, 前缀索引 j = 0, 后缀索引 i = 0.
  36. 比较前缀和后缀当前字符:
  37. 如果相同, 长度值加一, A[i] = j + 1, 即在前一个字符后缀重合长度基础上加一, 前后缀指针各自加一.
  38. 如果不同,
  39. - 如果 j > 0: j = A[j - 1], 更新前缀指针, 从已经匹配的子串 的 (最长)前后缀相同的前缀尾部开始匹配;
  40.   . i.e. "a.a." 的重合前后缀是 "a.", (最长)重合前后缀, 是一个 更短的子串.
  41. - 如果 j == 0: 没有重复前后缀, A[i] = 0; i += 1, 源字符串的起始查找位置,前进一位.
  42.  
  43.  a  .   a  .  b   -  a  .   a   .   a   .  a  .   b  x  a  # 忽略空格. 加空格为了上下位置对应.
  44. [0, 0, 1, 2, 0, 0, 1, 2, 3, 4, 3, 4, 3, 4, 5, 0, 1] # suffix array.
  45.  0  1  2  3  4  5  6  7  8  9  10 ..
  46.  
  47. 例子:
  48. a 和 a.a.b-a 前缀是 a 后缀是 a , 前后缀重合长度是 1,
  49. a. 和 a.a.b-a. ,  后缀是 a. , 前后缀重合长度是 2,
  50. ..
  51. a.a. 和 a.a.b-a.a. 前缀是 a.a. 后缀是 a.a. , 前后缀重合长度是 4,
  52. a.a.b 和 a.a.b-a.a.a , 后缀结尾字符 a 和前缀尾 b 不同.
  53. 之前字符 . 的前后缀重合长度是 4,
  54. j = A[j - 1] = A[4 - 1] = 2, 即用 p[2] 匹配 s[i]
  55. 如果还是不匹配, j = A[j - 1] 迭代计算, 直到 j == 0.
  56.  
  57. i, j = 1, 0
  58. while i < n:
  59.     if p[i] == p[j]:
  60.         A[i] = j + 1 # A[i] = A[i - 1] + 1; 和 j + 1 一样的效果.
  61.         j += 1
  62.         i += 1
  63.     else:
  64.         if j > 0:
  65.             j = A[j - 1]
  66.         else:
  67.             A[i] = 0
  68.             i += 1
  69. return A
  70. .
  71.  
  72. class Solution:
  73.     """
  74.    """
  75.     def strStr2(self, s, p):
  76.         if p is None or s is None:
  77.             return -1
  78.         if p == "":
  79.             return 0
  80.         A = self.suffix(p)
  81.         n = len(s)
  82.         i, j = 0, 0
  83.         ans, t = -1, 0
  84.         while i < n:
  85.             # t += 1
  86.             if s[i] == p[j]:
  87.                 if j == len(p) - 1:
  88.                     ans = i - j
  89.                     break
  90.                 j += 1
  91.                 i += 1
  92.             else:
  93.                 if j > 0:
  94.                     j = A[j - 1]
  95.                 else:
  96.                     i += 1
  97.         # print(f"time:  {t}, n {n}")
  98.         return ans
  99.    
  100.     def suffix(self, p):
  101.         n = len(p)
  102.         A = [0] * n
  103.         i, j = 1, 0
  104.         while i < n:
  105.             if p[i] == p[j]:
  106.                 A[i] = j + 1
  107.                 j += 1
  108.                 i += 1
  109.             else:
  110.                 if j > 0:
  111.                     # print(j, A[j - 1], p[j], p[A[j - 1]])
  112.                     j = A[j - 1]
  113.                 else:
  114.                     A[i] = 0
  115.                     i += 1
  116.         return A
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement