• 字符串旋转

    将字符串前面若干个字符移到字符串的尾部, 例如abcdef,将前面三个字符进行旋转后就是defabc

    核心思想: 三步反转,两字符串分为两个部分,各自反转,然后整体反转。例如上面这个例子:

    abc → cba、def → fed,合并起来就是cbafed,最后整体反转defabc。

    
    
  • 字符串包含

  • 字符串匹配

    KMP算法,核心思想就是匹配失败的时候,不重头重新匹配,而是有选择的跳过匹配失败的地方,从下一个地方开始匹配。例如: 字符串BADBARBARD、要匹配的模式: BARD,当匹配到R的时候失败了,没必要重头从A来匹配,直接从匹配失败的地方开始重新匹配。但如果字符串是BABABABCABC、模式是: ABABC,那么当匹配到C失败后,我们不能直接从当前位置开始,这样会漏掉ABABC。这是因为模式本身实际是有边界的。ABAB,这里AB就是边界。对于有边界的字符我们应从失败的地方回退几个字符,这几个字符肯定是能匹配的,因为这几个字符是边界,前后都一样,因此我只要从边界的下一个字符开始继续匹配就可以了。最后这个问题就转换成如何求模式本身的边界问题了。

    def kmp_search(text, pattern):
        """KMP字符串搜索算法实现"""
        if not pattern:
            return 0  # 如果模式为空字符串,我们认为它在索引0处匹配
    
        # 计算部分匹配表
        lsp = compute_lsp_table(pattern)
    
        j = 0  # text的索引
        k = 0  # pattern的索引
        while j < len(text):
            if text[j] == pattern[k]:
                j += 1
                k += 1
                if k == len(pattern):
                    return j - k  # 找到匹配
            elif k > 0:
                k = lsp[k - 1]  # 不匹配,进行跳转
            else:
                j += 1
    
        return -1  # 没有找到匹配
    
  • 字符串前后缀

    计算字符串的最长相同的前后缀表,类似KMP算法,找前后缀的过程就跟找pattern一样。

    def compute_lsp_table(pattern):
        lsp = [0] * len(pattern)  # 初始化LSP表
        j = 0  # 前缀的长度
    		
        for i in range(1, len(pattern)):
    				// 如果下一个字符不相同,那么当前位置的前缀就是前一个字符的最大前缀值
            while j > 0 and pattern[i] != pattern[j]:
                j = lsp[j - 1]  # 不匹配,跳转到前一个LSP
    				
    				// i == j,前后缀找到相同字符了,继续找下一个字符,j + 1
            if pattern[i] == pattern[j]:
                j += 1
    				
            lsp[i] = j  # 设置LSP表的当前位置
    
        return lsp