leetcode 字符串

目录

  1. 无重复字符的最长子串
  2. 最长回文子串
  3. 最长公共前缀
  4. 有效的括号
  5. 最长有效括号
  6. 字母异位词分组
  7. 最后一个单词的长度
  8. 编辑距离
  9. 最长回文串
  10. 重复的子字符串
  11. 不同字符的最小子序列
  12. 字符串的最大公因子
  13. 字符串中的第一个唯一字符
  14. 字符串解码
  15. 自重复子串
  16. 回文对

3. 无重复字符的最长子串
输入: s = “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
滑动窗口:

## 方法一:使用数组作为滑动窗口
class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        if not s :
            return 0
        window=[]   # 滑动窗口数组
        max_length=0
        for c in s :
            if c not in window:  # 如果字符不在滑动窗口中,则直接扩展窗口
                window.append(c)
            else:  # 从窗口中移除重复字符及之前的字符串部分,新字符串即为无重复字符的字符串
                window=window[window.index(c)+1:]
                window.append(c)             # 扩展窗口
            max_length=max(max_length,len(window))
        return max_length
## 方法 二 :双指针优化
class Solution:
    # 双指针方法:使用数组索引,标记滑动窗口
    def lengthOfLongestSubstring(self, s: str) -> int:
        if not s :
            return 0
        max_length=0
        left,right=0,0
        for i,c in enumerate(s):
            if c not in s[left:right]:
                right+=1
            else:
                left+=s[left:right].index(c)+1   # index得到的是在窗口数组内的下标
                right+=1
            max_length=max(right-left,max_length)
        return max_length       
## 方法三: hash表优化
class Solution:
    # 使用Hash(字典) - 滑动窗口优化
    def lengthOfLongestSubstring(self, s: str) -> int:
        ignore_index=-1
        dic={}          #  任意字符最后出现的索引位置
        max_length=0
        for i,c in enumerate(s):
            if c in dic and dic[c] > ignore_index:
                ignore_index=dic[c]
                dic[c]=i
            else:
                dic[c]=i
                max_length=max(i-ignore_index,max_length)
        return max_length

5. 最长回文子串

解题代码:

class Solution(object):
    def longestPalindrome(self, s):
        """
        :type s: str
        :rtype: str
        """
        n = len(s)
        maxl = 0
        start = 0
        for i in range(n):
            if i - maxl >= 1 and s[i-maxl-1: i+1] == s[i-maxl-1: i+1][::-1]:
                start = i - maxl - 1
                maxl += 2
                continue
            if i - maxl >= 0 and s[i-maxl: i+1] == s[i-maxl: i+1][::-1]:
                start = i - maxl
                maxl += 1
        return s[start: start + maxl]

14. 最长公共前缀

编写一个函数来查找字符串数组中的最长公共前缀。

如果不存在公共前缀,返回空字符串 “”。

解题代码:

class Solution:
    def longestCommonPrefix(self, strs):
        """
        :type strs: List[str]
        :rtype: str
        """
        res = ""
        if len(strs) == 0:
            return ""
        for each in zip(*strs):#zip()函数用于将可迭代对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表
            if len(set(each)) == 1:#利用集合创建一个无序不重复元素集
                res += each[0]
            else:
                return res
        return res
###  方法二
class Solution:
    def longestCommonPrefix(self, strs: List[str]) -> str:
        if not strs: return ""
        str0 = min(strs)
        str1 = max(strs)
        for i in range(len(str0)):
            if str0[i] != str1[i]:
                return str0[:i]
        return str0

20. 有效的括号

代码如下:

class Solution(object):
    def isValid(self, s):
        """
        :type s: str
        :rtype: bool
        """
        mapping={'(':')','[':']','{':'}'}
        stack=[]
        for char in s:
            if char in mapping:
                stack.append(char)
            elif char in mapping.values():
                if len(stack)==0 or mapping.get(stack[-1])!=char:
                    return False
                else:
                    stack.pop()
        if len(stack)==0:
            return True
        else:
            return False
        

32. 最长有效括号
给你一个只包含 ‘(’ 和 ‘)’ 的字符串,找出最长有效(格式正确且连续)括号子串的长度。

输入:s = ")()())"
输出:4
解释:最长有效括号子串是 "()()"
class Solution:
    def longestValidParentheses(self, s: str) -> int:
        if not s: 
            return 0
        stack = []
        ans = 0
        for i in range(len(s)):
            # 入栈条件:一、入栈条件为1.栈为空 2.当前字符是'(' 3.栈顶符号位')'
            if not stack or s[i] == '(' or s[stack[-1]] == ')':
                stack.append(i)     # 下标入栈
            else:
                # 计算结果
                stack.pop()
                ans = max(ans, i - (stack[-1] if stack else -1))
        return ans

49. 字母异位词分组

class Solution:
    def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
        dict = {}
        for item in strs:
            key = ''.join(sorted(item))
            dict[key] = dict.get(key, []) + [item]
        return list(dict.values())

58. 最后一个单词的长度

代码如下:

class Solution:
    def lengthOfLastWord(self, s):
        """
        :type s: str
        :rtype: int
        """
        str = ''
        count = 0
        for i in s[::-1]:
            if str != '' and i==' ':
                return count
            if i != ' ':
                count = count+ 1
                str = str + i
        return count

72. 编辑距离
给你两个单词 word1 和 word2,请你计算出将 word1 转换成 word2 所使用的最少操作数 。

你可以对一个单词进行如下三种操作:

  1. 插入一个字符
  2. 删除一个字符
  3. 替换一个字符
输入:word1 = "horse", word2 = "ros"
输出:3
解释:
horse -> rorse (将 'h' 替换为 'r')
rorse -> rose (删除 'r')
rose -> ros (删除 'e')

# 基于动态规划的解法
def edit_dist(str1,str2):
    m,n=len(str1),len(str2)

    dp=[[0]*(n+1) for i in range(m+1)]
    for i in range(m+1):
        for j in range(n+1):
            # 假设第一个字符串为空,则转换的代价为j(插入次数)
            if i==0:
                dp[i][j]=j
            elif j==0:
                dp[i][j]=i        # 删除的次数
            # 如果最后一个字符相等,则不会产生转换代价
            elif str1[i-1]==str2[j-1]:
                dp[i][j]=dp[i-1][j-1]
            else:
                # 如果最后一个字符换不同,则考虑多种可能
                dp[i][j]=1+min(dp[i][j-1],    # 插入,长度i的字符串以及和j-1的字符串对齐,转变为长度为j的只需要插入一个元素
                               dp[i-1][j],    # 删除
                               dp[i-1][j-1]   # 替换
                               )
    return dp[m][n]

409.最长回文串

class Solution(object):
    def longestPalindrome(self, s):
        """
        :type s: str
        :rtype: int
        """
        dict={}
        for c in s:
            if c in dict:
                dict[c] +=1
            else:
                dict[c]=1
        res=0
        for  value in dict.values():
            res+=value/2*2
        if res<len(s):
            return res+1
        return res

459. 重复的子字符串

class Solution(object):
    def repeatedSubstringPattern(self, s):
        """
        :type s: str
        :rtype: bool
        """
        n=len(s)
        for i in range(1,n):
            base=s[:i]
            if base*(n/i)==s:
                return True
        return False

1081. 不同字符的最小子序列
返回 s 字典序最小的子序列,该子序列包含 s 的所有不同字符,且只包含一次。
示例 2:
输入:s = “cbacdcbc”
输出:“acdb”
字典序
解题思路参考
代码

class Solution(object):
    def smallestSubsequence(self, text):
        """
        :type text: str
        :rtype: str
        """
        size = len(text)
        stack = []
        for i in range(size):
            if text[i] in stack:
                continue
            while stack and ord(text[i]) < ord(stack[-1]) and \
                    text.find(stack[-1], i) != -1:
                stack.pop()
            stack.append(text[i])
        return ''.join(stack)
class Solution:
    def smallestSubsequence(self, s: str) -> str:
        # 栈 + hash
        ans, d = [' '], set()
        for i, c in enumerate(s):
            if c not in d:
                while ans[-1] > c and ans[-1] in s[i: ]:
                    d.remove(ans.pop())
                ans += [c]
                d |= {c}
        return ''.join(ans[1: ])

1071. 字符串的最大公因子
解题思路:辗转相除法
代码如下:

class Solution(object):
    def gcdOfStrings(self, str1, str2):
        """
        :type str1: str
        :type str2: str
        :rtype: str
        """
        l1, l2 = len(str1), len(str2)
        if l2 > l1:
            str1, str2 = str2, str1
            l1, l2 = l2, l1
        if l1 == l2:
            return str1 if str1==str2 else ''        
        f = l1%l2         
        if f == 0:            
            return str2 if str2*(l1//l2)==str1 else ''
        else:
            return self.gcdOfStrings(str2, str1[-f:])

387. 字符串中的第一个唯一字符

给定一个字符串,找到它的第一个不重复的字符,并返回它的索引。如果不存在,则返回 -1。

案例:

s = "leetcode"
返回 0.

s = "loveleetcode",
返回 2.

代码:

class Solution(object):
    def firstUniqChar(self, s):
        """
        :type s: str
        :rtype: int
        """
        dic = dict()
        for i in s:
            if i in dic:
                dic[i] = dic[i] + 1
            else:
                dic[i] = 1
        for i in range(len(s)):
            if dic[s[i]] == 1:
                return i
        return -1

BM算法:

# 坏字符规则:后移位数=坏字符的位置- 搜索次中上一次出现的位置
def getBMBC(pattern):
    # 生成坏字符表
    BMBC = {}
    for i in range(len(pattern) - 1):
        char = pattern[i]
        BMBC[char] = i + 1  # 记录坏字符最右位置(不包括模式串最右侧字符)
    return BMBC


# 好后缀规则:后移位数=好后缀的位置- 搜索次中上一次出现的位置
""" 所有的"好后缀"(MPLE、PLE、LE、E)之中,只有"E"在"EXAMPLE"还出现在头部,所以后移 6 - 0 = 6位"""

def getBMGS(pattern):
    BMGS = {}  # 好后缀表
    # 无后缀仅根据坏字符
    BMGS[''] = 0
    for i in range(len(pattern)):
        # 好后缀
        GS = pattern[len(pattern) - i - 1:]
        for j in range(len(pattern) - i - 1):
            NGS = pattern[j:j + i + 1]  # 匹配部分
            if GS == NGS:  # 记录模式串中好后缀最靠右的位置(除结尾)
                BMGS[GS] = len(pattern) - j - i - 1
    return BMGS


def BM(string, pattern):
    m = len(pattern)
    n = len(string)
    i = 0
    j = m
    indies = []
    BMBC = getBMBC(pattern)
    BMGS = getBMGS(pattern)
    while i < n:
        while j > 0:
            if i + j - 1 >= n:
                return indies

            a = string[i + j - 1:i + m]  # 主串判断匹配模式
            b = pattern[j - 1:]  # 模式判断匹配模式
            if a == b:  # 当前匹配成功则继续匹配
                j -= 1
            else:
                i = i + max(BMGS.setdefault(b[1:], m), j - BMBC.setdefault(string[i + j - 1], 0))
                j = m

            if j == 0:  # 匹配成功
                indies.append(i)
                i += 1
                j = len(pattern)


if __name__ == "__main__":
    string = "here is a simple example"
    pattern = "example"
    print(BM(string, pattern))

KMP算法

#kmp算法解决:子字符串匹配的题,使用当前比较字符之前的最长相同的前后缀,然后将前缀与上面的长字符串对齐,继续比较后面的字符串
"""
'部分匹配'的实质,有时候,字符串的头部和尾部会有重复。比如 ABCDAB 之中有两个AB,那么它的的部分匹配就是2(AB的长度)
移动搜索词的时候,第一个‘AB’向后移动4位(已匹配的字符数-部分匹配值),就可以来到第二个AB的位置
"""
def same_start_end(s):
    """最长前后缀相同的字符位数 """
    n=len(s)  #整个字符串长度
    j=0   # 前缀匹配指向
    i=1   # 后缀匹配指向
    result_list=[0]*n
    while i<n:
        if s[j]==s[i]:        #相同则继续比较
            j=j+1
            result_list[i]=j
            i+=1
        elif j!=0:                      # 比较不相等,将j值设置为j前一位的result_list中的值,为了在之前匹配到的子串中找到最长相同前后缀, 最简单理解思维为:j前缀指针重新回到0位置
            j=result_list[j-1]
        else:                          # 比较不相等并且此时比较的已经是第一个字符
            result_list[i]=0
            i+=1
    return result_list

def kmp(s,p):
    #kmp算法,s是字符串,p是模式字符串,返回值为匹配的第一个字符串的第一个字符的索引,没有匹配到返回-1
    s_length=len(s)
    p_length=len(p)
    i=0   #指向s
    j=0 # 指向p
    next=same_start_end(p)                # next数组中,next[i]表示的是前i个字符组成的这个子串最长的相同的前后缀的长度
    while (i< s_length) and (j<p_length):
        if  s[i] ==p[j]: # 对应字符相同    
            i+=1  
            j+=1
        elif j!=0:           #与模式比较的是模式的第一个字符
            j=next[j-1]   
        else:                    #取模式当前字符之前最长相同前后缀的前缀的后一个字符继续进行比较
            i+=1
    if j ==p_length:  #匹配成功
        return i-j
    return -1              

    
#测试
s="ababababca"
p="abababca"
print(kmp(s,p))

394. 字符串解码

输入:s = "3[a]2[bc]"
输出:"aaabcbc"

算法流程:

构建辅助栈 stack, 遍历字符串 s 中每个字符 c;

  • 当 c 为数字时,将数字字符转化为数字 multi,用于后续倍数计算;
  • 当 c 为字母时,在 res 尾部添加 c;
  • 当 c 为 [ 时,将当前 multi 和 res 入栈,并分别置空置 0:
  • 记录此 [ 前的临时结果 res 至栈,用于发现对应 ] 后的拼接操作;
    • 记录此 [ 前的倍数 multi 至栈,用于发现对应 ] 后,获取 multi × […] 字符串。
    • 进入到新 [ 后,res 和 multi 重新记录。
  • 当 c 为 ] 时,stack 出栈,拼接字符串 res = last_res + cur_multi * res,其中:
    • last_res是上个 [ 到当前 [ 的字符串,例如 “3[a2[c]]” 中的 a;
    • cur_multi是当前 [ 到 ] 内字符串的重复倍数,例如 “3[a2[c]]” 中的 2。

返回字符串 res。

class Solution:
    def decodeString(self, s: str) -> str:
        stack, res, multi = [], "", 0
        for c in s:
            if c == '[':
                stack.append([multi, res])
                res, multi = "", 0
            elif c == ']':
                cur_multi, last_res = stack.pop()
                res = last_res + cur_multi * res
            elif '0' <= c <= '9':
                multi = multi * 10 + int(c)            
            else:
                res += c
        return res

1. 自重复子串
一个自重复串是一个字符串,其前一半和后一半是一样的,例如 abcdbabcdb (长度一定是偶数)。
输入一个字符串,找出其中最长的自重复子串。这里的子串要求连续。

例子

输入:
abababcdabcd
输出:
abcdabcd
def longestDupSubstring(s):
    maxlen=0
    res=""
    for i in range(len(s)):
        for j in range(i+1,len(s)):
            reg=s[i:j]
            if j+1<len(s) and j+j-1-i <len(s):
                if reg==s[j:j+j-i] and len(reg) >maxlen:
                    maxlen=len(reg)
                    res=reg
    return res

336. 回文对
前缀与后缀的解法

class Solution:
    def palindromePairs1(self, words: List[str]) -> List[List[int]]:
        res = []
        worddict = {word: i for i, word in enumerate(words)}  # 构建一个字典,key为word,value为索引
        for i, word in enumerate(words):
            for j in range(len(word)+1):    
                tmp1 = word[:j]  # 字符串的前缀
                tmp2 = word[j:]  # 字符串的后缀
                if tmp1[::-1] in worddict and worddict[tmp1[::-1]] != i and tmp2 == tmp2[::-1]:
                    res.append([i, worddict[tmp1[::-1]]])  

                if j > 0 and tmp2[::-1] in worddict and worddict[tmp2[::-1]] != i and tmp1 == tmp1[::-1]:         
                    res.append([worddict[tmp2[::-1]], i])  # 返回此时的word下标和找到的字符串下标
        return res

你可能感兴趣的:(算法,自然语言处理,机器学习,算法)