11.字符串
  # ① 栈(Stack)
 # 01.有效的括号
# 事例1
输入:s = "()[]{}"
输出:true
# 事例2
输入:s = "(]"
输出:false
 2
3
4
5
6
1、Python
- 如果是左括号直接入栈,如果右边括号判断栈顶是否时对应左括号
 - 如果 左右括号匹配 栈顶出栈,最终判断是否栈为空
 
class Solution:
    def isValid(self, s):
        d = {
            ")": "(",
            "]": "[",
            "}": "{",
        }
        stack = []
        for ch in s:
            if ch in d:        # 如果字符串是 右边括号判断栈顶是否时对应左括号
                if not stack or stack[-1] != d[ch]:
                    return False
                stack.pop()   # 如果 左右括号匹配 栈顶出栈
            else:
                stack.append(ch)  # 如果是左侧括号,直接入栈
        return not stack
if __name__== "__main__":
    print(Solution().isValid("([]{})"))  # True
    print(Solution().isValid("([(])"))  # False
 2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 02.最长有效括号
输入:s = "(()"
输出:2
解释:最长有效括号子串是 "()"
输入:s = ")()())"
输出:4
解释:最长有效括号子串是 "()()"
 2
3
4
5
6
7
1、Python
- 使用一个栈来记录索引,初始时我们将 
-1压入栈中,这可以帮助我们处理最开始的有效子串 - 遍历字符串
s中的每一个字符:- 如果字符是 
'(',我们将它的索引压入栈中 - 如果字符是 
')',我们弹出栈顶的元素如果此时栈为空,说明没有匹配的'(',我们将当前索引压入栈中 - 如果栈不为空,我们计算当前有效括号的长度,即当前索引减去栈顶元素的索引,并更新最大长度
 
 - 如果字符是 
 - 遍历结束后,最大长度即为最长有效括号的长度
 
class Solution:
    def longestValidParentheses(self, s):
        stack = [-1]  # 栈初始存入 -1 以便计算长度
        max_len = 0
        for i in range(len(s)):
            if s[i] == '(':
                stack.append(i)
            else:
                stack.pop()  # stack为空 证明 当栈顶只有一个元素时出现了右括号,必然不符合要求[-1,)]
                if not stack:
                    stack.append(i) # 如果栈为空,压入当前索引
                else:
                    max_len = max(max_len, i - stack[-1])  # 计算最大长度
        return max_len
print(Solution().longestValidParentheses("(()"))     # 输出:2
print(Solution().longestValidParentheses(")()())"))  # 输出:4
 2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 03.反转字符串中的单词 III
给定一个字符串,你需要反转字符串中每个单词的字符顺序,同时仍保留空格和单词的初始顺序
输入:"Let's take LeetCode contest"
输出:"s'teL ekat edoCteeL tsetnoc"
 2
1、Python
利用栈的先进后出的结构很容易做到字符反转
但一串英文单词中,单词的个数总是比空格的字数多一个,在处理的时候很不方便
- 我们不妨在原字符串末尾补充一个空格,让每一小节都变成“单词+空格”
 - 遇见空格就是我们判断出栈的前提条件
 - 于是我们只要利用切片返回第一个字符后面的string就可以了
 
def reverseWords(s):
    stack = []   # 使用列表模拟栈
    new_s = ""   # new_s 新字符串
    s = s + " "  # 原字符串末尾补充一个空格,让每一小节都变成“单词+空格”
    for i in s:
        stack.append(i)  # 只要没有遇到空格就依次进栈
        if i == " ":     # 如果遇到空格依次出栈,这样就实现了反转
            while(stack):
                new_s += stack.pop()
    return new_s[1:]
print( reverseWords( "I love you" ) )  # I evol uoy
 2
3
4
5
6
7
8
9
10
11
12
2、Python基本反转
'''第一种:使用字符串切片'''
s = 'Hello World'
print(s[::-1])  # dlroW olleH
'''第二种:使用列表的reverse方法'''
l = list(s)
l.reverse()
print( "".join(l) )   # dlroW olleH
'''第三种:使用reduce'''
from functools import reduce
result = reduce(lambda x,y:y+x,s)
print( result )      # dlroW olleH
 2
3
4
5
6
7
8
9
10
11
12
13
# 04.去除重复字母
-  
- 给你一个字符串 
s,请你去除字符串中重复的字母,使得每个字母只出现一次 - 需保证 返回结果的
字典序最小(要求不能打乱其他字符的相对位置) 
 - 给你一个字符串 
 1081.不同字符的最小子序列 (opens new window)
- 返回 
s字典序最小的子序列,该子序列包含s的所有不同字符,且只包含一次 
- 返回 
 
输入:s = "bcabc"
输出:"abc"
输入:s = "cbacdcbc"
输出:"acdb"
 2
3
4
5
1、Python
- 使用 stack 作为
单调栈,存储最终结果 - 如果 
1.当前字符 < 栈顶字符2.栈顶字符后面还出现(dic_cnt[stack[-1]] > 0) - 证明 
使用当前元素替换栈顶元素会使字典序列更小,替换即可 
class Solution:
    def smallestSubsequence(self, s: str) -> str:
        dic_cnt = {}  # 每个字符出现次数: {'c': 4, 'b': 2, 'a': 1, 'd': 1}
        for c in s:
            dic_cnt[c] = dic_cnt.get(c, 0) + 1
        set_ = set()  # set_ 记录栈中的字符,确保字符不重复
        stack = []    # stack 作为单调栈,存储最终结果,保证字典序最小
        for c in s:
            dic_cnt[c] -= 1  # 当前字符的计数减1,表示它已被访问一次
            if c in set_:    # 如果字符已在栈中,跳过,避免重复
                continue
            # 1. 当前字符 < 栈顶字符  2. 栈顶字符后面还出现 (dic_cnt[stack[-1]] > 0)
            while stack and c < stack[-1] and dic_cnt[stack[-1]] > 0:
                set_.remove(stack.pop())            # 弹出栈顶换当前字符(会更小)
            stack.append(c)            # 将当前字符加入栈,并标记为已加入
            set_.add(c)
        return ''.join(stack)
print(Solution().smallestSubsequence("cbacdcbc"))  # 输出: "acdb"
 2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 05.字符串解码
给定一个经过编码的字符串,返回它解码后的字符串
编码规则为:
k[encoded_string],表示其中方括号内部的encoded_string正好重复k次你可以认为原始数据不包含数字,所有的数字只表示重复的次数
k,例如不会出现像3a或2[4]的输入
输入:s = "3[a]2[bc]"
输出:"aaabcbc"
输入:s = "3[a2[c]]"
输出:"accaccacc"
 2
3
4
5
1、Python
- 使用 栈 处理嵌套的重复字符串
 - 当遇到 
[时,保存当前字符串和重复次数 - 遇到 
]时,弹出栈顶并拼接重复子串 - 普通字符直接追加到当前字符串最终构造出解码后的字符串
 
def decodeString(s):
    stack = []            # 存储左右括号 和临时结果 [('b', 3), ('a', 2)]
    cur_str = ''          # 存储当前的解析字符串
    cur_num = 0           # 解析字符数量
    for c in s:
        print(c, stack, cur_str)
        if c.isdigit():
            cur_num = cur_num * 10 + int(c)   # 处理多位数字,计算重复次数
        elif c == '[':      # 遇到左括号,将当前字符串和数字存入栈
            stack.append((cur_str, cur_num))  # 左括号 [('b', 3), ('a', 2)]
            cur_str = ''    # 重置当前字符串
            cur_num = 0     # 重置数量
        elif c == ']':      # 遇到右括号,计算栈顶子串
            last_str, num = stack.pop()          # last_str, num =  ('a', 2)
            cur_str = last_str + cur_str * num   # 字符串拼接 a + c * 2 = acc
        else:
            cur_str += c     # 普通字符直接拼接到当前字符串
    return cur_str
print(decodeString("b3[a2[c]]"))  # 输出: "baccaccacc" (b acc acc acc)
"""
b [] 
3 [] b
[ [] b
a [('b', 3)] 
2 [('b', 3)] a
[ [('b', 3)] a
c [('b', 3), ('a', 2)] 
] [('b', 3), ('a', 2)] c     # 遇到第一个 ] 出栈 "('a', 2)] c" =  a + c * 2 = acc 
] [('b', 3)] acc             # 遇到第二个 ] 出栈 "[('b', 3)] acc" =  b + accc * 3 = baccaccacc
"""
 2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 06.移掉K位数字
给你一个以字符串表示的非负整数
num和一个整数k,移除这个数中的k位数字,使得剩下的数字最小请你以字符串形式返回这个最小的数字
输入:num = "10200", k = 1
输出:"200"
解释:移掉首位的 1 剩下的数字为 200. 注意输出不能有任何前导零
输入:num = "1432219", k = 3
输出:"1219"
 2
3
4
5
6
1、Python
- 遍历每个数字时,我们将其与栈顶数字比较
 - 如果栈顶数字大于当前数字,并且我们还有机会移除数字(
k > 0),则移除栈顶数字 - 这个过程确保我们逐步构建的数字尽可能小
 
class Solution:
    def removeKdigits(self, num, k):
        stack = []
        for digit in num:    # 构建单调递增的数字串
            while k > 0 and stack and stack[-1] > digit:   # k>0保证最多移除k个数
                stack.pop()
                k -= 1
            stack.append(digit)
        # 最终 stack = ['0', '0', '2', '2', '1', '9'], 移除首位1 和 43这三个字符
        if k > 0:
            # l=[1,2,3,4,5],  l[:-2]=[1, 2, 3]
            stack = stack[:-k]  # 如果 K > 0,删除末尾的 K 个字符
        return "".join(stack).lstrip('0') or "0"    # 抹去前导零,如果最终结果为空,则返回 "0"
print( Solution().removeKdigits("100224319", 3)) # 2219
 2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 07.基本计算器
- 224.基本计算器 (opens new window)
 - 给你一个字符串表达式 
s,请你实现一个基本计算器来计算并返回它的值 s由数字+-()空格组成- '+' 不能用作一元运算(例如, "+1" 和 
"+(2 + 3)"无效) - '-' 可以用作一元运算(即 "-1" 和 
"-(2 + 3)"是有效的) - 输入中不存在两个连续的操作符
 
1、Python
- 用栈存储括号外的运算结果和符号(栈中这种 
3+4+5+6会直接算出结果存储到cur_res中) - 遍历字符串,字符串根据字符串下面四种情况匹配:
(+-) - ① 
(:计算cur_res结果 并将结果入栈,同时cur_res=0 sign=+ - ② 
数字:计算cur_num当前数字(可能有多位) - ③ 
+-: 计算cur_res并设置sign=char cur_num=0 - ④ 
):遇到右括号证明 匹配到了一个完整左右括号,出栈 计算结果 
class Solution:
    def calculate(self, s):
        stack = []                # 用栈存储括号外的运算结果和符号
        cur_res = 0               # 当前计算的累积结果
        cur_num = 0               # 当前正在处理的数字
        sign_map = {"+": 1, "-": -1}  # 使用字典来映射符号
        sign = "+"                # 当前符号,初始为 "+"
        for char in s:
            if char == '(':         # 1)入栈 计算cur_res结果 并将结果入栈
                stack.append(cur_res)                   # 将当前结果入栈保存
                stack.append(sign)
                cur_res = 0
                sign = "+"
            elif char.isdigit():    # 2)计算cur_num当前数字(可能有多位)
                cur_num = cur_num * 10 + int(char)  # 处理多位数字的情况
                
            elif char in sign_map:  # 3)计算cur_res
                cur_res += sign_map[sign] * cur_num     # 将当前数字和符号累加到结果中
                sign = char                             # 直接存储当前符号,稍后处理
                cur_num = 0
                
            elif char == ')':       # 4)出栈 括号内最后一个数字 + 累加栈顶值
                cur_res += sign_map[sign] * cur_num     # 处理括号内最后一个数字
                sign = stack.pop()
                # 将括号内结果乘以栈中的符号,并累加栈中的结果
                cur_res = stack.pop() + sign_map[sign] * cur_res
                cur_num = 0
            print(f"{char}\t{stack}\t{cur_res}")        # 打印当前字符、栈和累积结果
        return cur_res + sign_map[sign] * cur_num       # 10 处理最后一个数,并返回
print(Solution().calculate("(1+(2+(3+4+5+6)))"))
 2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
- 推演 
"(1+(2+(3+4+5+6)))" 
# print(f"{char}\t{stack}\t{cur_res}")
(	[0, '+']	0
1	[0, '+']	0
+	[0, '+']	1
(	[0, '+', 1, '+']	0
2	[0, '+', 1, '+']	0
+	[0, '+', 1, '+']	2
(	[0, '+', 1, '+', 2, '+']	0
3	[0, '+', 1, '+', 2, '+']	0
+	[0, '+', 1, '+', 2, '+']	3   # cur_res=3
4	[0, '+', 1, '+', 2, '+']	3
+	[0, '+', 1, '+', 2, '+']	7   # cur_res = cur_res+4 = 7
5	[0, '+', 1, '+', 2, '+']	7
+	[0, '+', 1, '+', 2, '+']	12  # cur_res = cur_res+5 = 12
6	[0, '+', 1, '+', 2, '+']	12
)	[0, '+', 1, '+']	20
)	[0, '+']	21
)	[]	21
 2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# ② 回文
 # 11.回文数
给你一个整数 x ,如果 x 是一个回文整数,返回 true ;否则,返回 false
回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数例如,121 是回文,而 123 不是
输入:x = 121
输出:true
输入:x = -121
输出:false
 2
3
4
1、Python
class Solution:
    def isPalindrome(self, x):
        s = str(x)
        l, r = 0, len(s) - 1
        while l < r:
            if s[l] != s[r]:  # 只要有一处不匹配,就不是回文
                return False
            l += 1
            r -= 1
        return True  # 所有字符匹配,返回 True
print(Solution().isPalindrome(1221))  # True
 2
3
4
5
6
7
8
9
10
11
12
13
# 12.最长回文串
给定一个包含大写字母和小写字母的字符串,找到通过这些字母构造成的最长的回文串
在构造过程中,请注意区分大小写比如
"Aa"不能当做一个回文字符串
输入:  "abccccdd
输出:  7
解释:  我们可以构造的最长的回文串是"dccaccd", 它的长度是 7
 2
3
1、Python
如果字符已经在集合中出现,就移除它,否则添加进去
len(s) - len(odd_chars)计算的是可以形成的最长偶数长度的回文如果集合中还有字符,那么可以再在中间添加一个字符构成奇数长度的回文
def longestStr(s):
    # 使用集合来记录字符的奇数次出现
    odd_chars = set()
    for char in s:
        if char in odd_chars:
            odd_chars.remove(char)
        else:
            odd_chars.add(char)
    
    # 如果有奇数字符,那么我们可以在回文的中心添加一个字符
    return len(s) - len(odd_chars) + (1 if odd_chars else 0)
# 示例用法
s = 'abcccccdd'
print(longestStr(s))  # 输出: 7
 2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 13.最长回文子串
给你一个字符串 s,找到 s 中最长的回文子串
输入:s = "babad"
输出:"bab"
解释:"aba" 同样是符合题意的答案
 2
3
1、Python
- 本题最容易想到的一种方法应该就是 
中心扩散法 - 从每一个位置出发,向两边扩散即可,遇到不是回文的时候结束
 
class Solution:
    def subscript(self, s, l, r):
        # 左指针大于等于0,右指针小于字符串长度,并且左右对称位置“值”相等,就继续循环
        while l >= 0 and r < len(s) and s[l] == s[r]:
            l -= 1
            r += 1
        return l + 1, r - 1
    def longestString(self, s):
        start, end = 0, 0           # 最开始,初始化左右位置都为 0号位置
        for i in range(len(s)):
            l1, r1 = self.subscript(s, i, i)     # 以单个字符为中心(奇数长度回文)
            l2, r2 = self.subscript(s, i, i + 1) # 以两个字符为中心(偶数长度回文)
            
            if r1 - l1 > end - start:  # 更新最长回文子串的索引
                start, end = l1, r1
            if r2 - l2 > end - start:
                start, end = l2, r2
        return s[start: end + 1]
print(Solution().longestString('babad'))
 2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# ③ 数学
 # 21.二进制求和
- 67.二进制求和 (opens new window)
 - 给你两个二进制字符串,返回它们的和(用二进制表示)
 - 输入为 非空 字符串且只包含数字 
1和0 
输入: a = "11", b = "1"
输出: "100"
输入: a = "1010", b = "1011"
输出: "10101"
 2
3
4
1、Python
def addBin(num1, num2):
    i, j = len(num1) - 1, len(num2) - 1  # 设定 i,j 两指针分别指向 num1,num2 尾部
    carry = 0    # 两数相加进位为0
    ret = ''     # 结果
    while i >= 0 or j >= 0:
        n1 = int(num1[i]) if i >= 0 else 0  # 获取 num1[i],超出范围则视为 0
        n2 = int(num2[j]) if j >= 0 else 0
        tmp = n1 + n2 + carry      # 计算当前位的和
        ret = str(tmp % 2) + ret   # 取余 就是当前位的值,并加入字符串右面
        carry = tmp // 2           # 井位
        i, j = i - 1, j - 1        # 指针向右移动一位
    if carry:                  # 如果循环结束还存在井位,在头部添加进位 1
        ret = '1' + ret
    return ret
print(addBin('11', '10'))   # '101' =》 5
 2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 22.36进制加法
- 36进制加法 (opens new window) 
头条补充题 - 36进制由0-9,a-z,共36个字符表示
 - 要求按照加法规则计算出任意两个36进制正整数的和,如
1b + 2x = 48(解释:47+105=152) - 要求:不允许使用先将36进制数字整体转为10进制,相加后再转回为36进制的做法
 
1、Python
- 使用两个字典,
char_to_value用于将字符映射到其对应的数值,value_to_char用于将数值映射回字符 - 根据上面转换,计算方法和正常字符串相加基本一致,
逐位相加即可 - 处理进位和结果反转: 
- 当循环结束时,可能会有一个进位剩余,如果有进位,我们会继续处理它
 - 由于计算结果是从低位到高位生成的,最终需要将 
res列表反转并转换为字符串 
 
class Solution:
    def __init__(self):
        #字符串转数字: {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5
        self.char_to_value = {ch: i for i, ch in enumerate("0123456789abcdefghijklmnopqrstuvwxyz")}
        #数字转字符串: 'u', 31: 'v', 32: 'w', 33: 'x', 34: 'y', 35: 'z'}
        self.value_to_char = {i: ch for i, ch in enumerate("0123456789abcdefghijklmnopqrstuvwxyz")}
    def addStrings(self, num1, num2):
        res = []
        i, j, carry = len(num1) - 1, len(num2) - 1, 0   # i 和 j 指向两个字符串的末尾,carry 表示进位
        while i >= 0 or j >= 0 or carry:
            # 取出当前位的字符并转换为数值
            n1 = self.char_to_value[num1[i]] if i >= 0 else 0
            n2 = self.char_to_value[num2[j]] if j >= 0 else 0
            tmp = n1 + n2 + carry                     # 计算当前位的和,包括进位
            res.append(self.value_to_char[tmp % 36])  # 当前位的值为 tmp % 36,找到对应的字符
            carry = tmp // 36                         # 更新进位值
            i -= 1
            j -= 1
        res.reverse()                 # 将结果列表反转并转换为字符串
        return ''.join(res)
print(Solution().addStrings("1b", "2x"))  # 输出:"48"
print(Solution().addStrings("1234", "123"))  # 输出:"1357"
 2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 23.字符串转换整数 (atoi)
请你来实现一个
myAtoi(string s)函数,使其能将字符串转换成一个32 位有符号整数跳过前导空格:忽略字符串开头的所有空格
处理符号:检查是否有符号字符(
+或-),并记录结果的符号读取数字:从当前位置开始读取数字字符,并转换为整数
处理溢出:确保结果不会超出 32 位有符号整数的范围
1、Python
class Solution:
    def myAtoi(self, s):
        i = 0
        while i < len(s) and s[i] == ' ':     #1.跳过前导空格
            i += 1
        if i < len(s) and (s[i] == '+' or s[i] == '-'): #2.处理符号
            sign = -1 if s[i] == '-' else 1
            i += 1
        else:
            sign = 1
        res = 0
        while i < len(s) and s[i].isdigit():        #3.读取数字
            digit = int(s[i])
            if res > (2 ** 31 - 1 - digit) // 10:   # 检查是否会溢出
                return 2 ** 31 - 1 if sign == 1 else -2 ** 31
            res = res * 10 + digit
            i += 1
        return res * sign
print( Solution().myAtoi("   -123") )  # -123
 2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# ④ 其他
 # 31.最长公共前缀
- 14.最长公共前缀 (opens new window)
 - 编写一个函数来查找字符串数组中的最长公共前缀
 - 如果不存在公共前缀,返回空字符串 
"" 
输入:strs = ["flower","flow","flight"]
输出:"fl"
输入:strs = ["dog","racecar","car"]
输出:""
解释:输入不存在公共前缀
 2
3
4
5
6
1、Python
- 将第一个字符串设为初始的最长公共前缀 
res - 从第二个字符串开始,逐个与当前的 
res进行比较 - 通过字符逐一比较 
res和当前字符串,找到它们的公共前缀长度l - 如果找到公共前缀,则更新 
res为该公共前缀;如果没有公共前缀,直接返回空字符串 
def longest(strs):
    res = strs[0]         # 初始化最长公共前缀 res 为第一个字符串
    for r in range(1, len(strs)):
        l = 0
        # 比较当前的 res(前缀)和 strs[r](当前字符串),找到它们的公共前缀
        while l < len(res) and l < len(strs[r]):
            if res[l] != strs[r][l]:
                break
            l += 1
        if l == 0:      # 如果没有公共前缀,直接返回空字符串
            return ""
        res = strs[0][:l]  # 更新 res 为当前找到的公共前缀
    return res
print(longest(["flower", "flow", "flight"]))    # 输出: fl
 2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 32.找出字符串中第一个匹配项的下标
给定一个 haystack 字符串和一个 needle字符串
在 haystack 字符串中找出needle字符串出现的第一个位置 (从0开始)
如果不存在,则返回 -1
# 示例 1:
输入: haystack  = "hello", needle = "ll"
输出: 2
# 示例 2:
输入: haystack  = "ababcababd", needleneedle = "ababd"
输出: -1
 2
3
4
5
6
7
1、Python KMP 算法
KMP 算法用于在字符串
haystack中查找needle的第一个匹配位置它的核心是
LPS(Longest Prefix Suffix,最长前缀后缀)数组needle = "ababd",最终LPS数组lps = [0, 0, 1, 2, 0]
通过 LPS,我们可以在失配时高效地跳转,避免重复匹配,提高搜索效率
class Solution:`needle = "ababd"`,最终 `LPS` 数组 `lps = [0, 0, 1, 2, 0]`
    def strStr(self, haystack, needle):
        """ KMP 主算法,查找 `needle` 在 `haystack` 中的第一个匹配位置 """
        next_arr = self.build_next(needle)  # [0, 0, 1, 2, 0]
        i, j = 0, 0  # 主串和子串的指针
        while i < len(haystack):
            if haystack[i] == needle[j]:  # 字符匹配,两个指针都后移
                i += 1
                j += 1
            elif j > 0:    # 失配时,跳过部分匹配
                j = next_arr[j - 1]
            else:          # 第一个字符失配,主串指针后移
                i += 1
            if j == len(needle):  # 匹配成功
                return i - j
        return -1  # 没有匹配
    def build_next(self, needle):
        lps = [0]  # 初始化 LPS 数组,第一个字符的前后缀长度必定是 0
        j = 0      # 记录最长公共前后缀的长度
        i = 1      # `i` 从 1 开始(因为 `lps[0]` 必定为 0)
        while i < len(needle):
            if needle[i] == needle[j]:  # 如果当前字符和前缀字符匹配
                j += 1         # 公共前后缀长度加 1
                lps.append(j)  # 记录当前子串的 LPS 值
                i += 1         # 继续向后遍历
            else:              # 如果不匹配
                if j == 0:
                    lps.append(0) # 如果没有任何公共前后缀,直接记 0
                    i += 1        # 继续遍历模式串
                else:
                    # 关键:回退到 `j` 的前一个 LPS 位置,尝试更短的公共前后缀
                    j = lps[j - 1]
        return lps  # [0, 0, 1, 2, 0]
print(Solution().strStr("ababcababd", "ababd"))  # 输出: 5
 2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
LPS 数组的作用是:
- 对于 
needle[0:i],找到最长的既是前缀又是后缀的子串长度(但不能是整个needle[0:i]本身) 
- 对于 
 示例:
needle = "ababd",最终LPS数组lps = [0, 0, 1, 2, 0]ineedle[0:i]前缀集合(不含完整串) 后缀集合 最长匹配前后缀 lps[i]0 A — — 无 0 1 AB A B 无 0 2 ABA A, ABBA, AA 1 3 ABAB A, AB, ABABAB, AB, BAB 2 4 ABABD A, AB, ABA, ABAB D, BD, ABD, BABD 无 0 
LPS 让
needle在失配时,不回到j=0,而是跳到已匹配前后缀的位置,避免重复匹配并且主串
i指针 不回退, 降低时间复杂度,从O(n*m)降到O(n+m)
2、Python 暴力破解
- 暴力破解
 
def strStr(haystack, needle):
    L, n = len(needle), len(haystack)  # L短字符串长度,n长字符串长度
    for start in range(n - L + 1):
        if haystack[start: start + L] == needle:
            return start
    return -1
print( strStr( 'hello', 'll' ) )    # 2
 2
3
4
5
6
7
8
# 33.字符串压缩
字符串压缩,利用字符重复出现的次数,编写一种方法,实现基本的字符串压缩功能
若“压缩”后的字符串没有变短,则返回原先的字符串
你可以假设字符串中只包含大小写英文字母(a至z)
 输入:"aabcccccaaa"
 输出:"a2b1c5a3"
 输入:"abbccd"
 输出:"abbccd"
 解释:"abbccd"压缩后为"a1b2c2d1",比原字符串长度更长
 2
3
4
5
6
1、Python
- 我们从左往右遍历字符串,用 ch记录当前要压缩的字符,cnt记录 ch出现的次数
 - 如果当前枚举到的字符 s[i]等于ch,我们就更新 cnt的计数,即 
cnt = cnt + 1 - 否则我们按题目要求将 ch以及cnt 更新到答案字符串ans 里,即 
ans = ans + ch + cnt,完成对 ch字符的压缩 - 随后更新 ch为 s[i],cnt 为 1,表示将压缩的字符更改为 s[i]
 - 在遍历结束之后,我们就得到了压缩后的字符串ans,并将其长度与原串长度进行比较
 - 如果长度没有变短,则返回原串,否则返回压缩后的字符串
 
def compressString(S):
    ch = S[0]      # ch记录当前要压缩的字符
    ans = ''       # ans拼接新字符串
    cnt = 0        # cnt记录 ch出现的次数
    for c in S:
        if c == ch:        # 当前枚举到的字符 s[i]等于ch,就加一
            cnt += 1
        else:              # 否则我们按题目要求将 ch以及cnt 更新到答案字符串ans 里
            ans += ch + str(cnt)  # `ans = ans + ch + cnt`,完成对 ch字符的压缩
            ch = c
            cnt = 1
    ans += ch + str(cnt)   # 跳出for循环后更新最后一部分
    return ans if len(ans) < len(S) else S
s = "aabcccccaaa"
print( compressString(s) )  # a2b1c5a3
 2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 34.统计单词次数
- 统计指定文件中单词出现数量,并找到出现次数最多的前五个
 
import re
def wordCount(path,n):
	d = {}
	with open(path, encoding="utf-8") as f:
		data = f.read()
		# words = ['version', '3', 'services', .... ]
		words = [s.lower() for s in re.findall("\w+", data)]
		for word in words:
			d[word] = d.get(word, 0) + 1
	return sorted(d.items(), key=lambda item: item[1], reverse=True)[:n]
if __name__ == '__main__':
	pt = r'C:\tmp\drf_demo\book\models.py'
	most_common_5 = wordCount(pt,5)
	print(most_common_5)  # [('mysql', 5), ('root', 3), ('db', 3), ('environment', 2), ('3306', 2)]
 2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 35.无重复字符的最长子串
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度
- 以(a)bcabcbb 开始的最长字符串为 (abc)abcbb;
 - 以 a(b)cabcbb 开始的最长字符串为 a(bca)bcbb
 - 以 ab(c)abcbb开始的最长字符串为ab(cab)cbb
 
输入: s = "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3
 2
3
1、Python
- 我们定义一个
左右指针,初始化都指向0位置 右指针每次从左指针位置向右滑动,当出现重复值时结束while循环并重新计算最大值
def longestSubstring(s):
	set_ = set()           # 哈希集合,记录每个字符是否出现过
	n = len(s)
	res = 0                # 记录最大子串长度
	for l in range(n):
		r = l
		while r < n and s[r] not in set_:  # 字符串遍历完成或者出现重复字符串结束
			set_.add(s[r])
			r += 1                           # 不断地移动右指针
		set_ = set()
		res = max(res, r - l)
	return res
print(longestSubstring("abcabcbb"))  # 3
 2
3
4
5
6
7
8
9
10
11
12
13
14