剑指offer2 字符串

字符串常和其他数据类型放一起考

数组

字符串下标天然是数组

后进先出,包括括号匹配、路径拆分等字符串题目,注意,python中的栈用list的append和pop实现栈的压入弹出

哈希表

主要是用来做快速匹配

队列

先进先出

字符串方法

  • 获取字符串长度: len(str)
  • 判断字符串是否相等: str== str2
  • 获取字符串的子串: str[begin: end:step​]
  • 拆分字符串: str.split(regex,maxsplit)
  • 获取下标: python 只有index()
  • 大小写转换: lower() upper()
  • 返回指定下标的字符:str[i]

 

剑指 Offer II 014. 字符串中的变位词

题目描述

给定两个字符串 s1 和 s2,写一个函数来判断 s2 是否包含 s1 的某个变位词。

换句话说,第一个字符串的排列之一是第二个字符串的 子串 。

剑指 Offer II 014. 字符串中的变位词 - 力扣(LeetCode) (leetcode-cn.com)

题解

滑动固定大小的窗口,双指针

用字符统计就可以做,遍历一次s2

def checkInclusion(self, s1: str, s2: str) -> bool:
    arr1, arr2, lg = [0] * 26, [0] * 26, len(s1)
    if lg > len(s2):
        return False
    # 维护一个arr2, 使它在s2上往右滑直到碰到尾巴
    for i in range(lg):
        arr1[ord(s1[i]) - ord('a')] += 1
        arr2[ord(s2[i]) - ord('a')] += 1

    for j in range(lg, len(s2)):
        if arr1 == arr2:
            return True
        arr2[ord(s2[j - lg]) - ord('a')] -= 1
        arr2[ord(s2[j]) - ord('a')] += 1
    return arr1 == arr2

剑指 Offer II 015. 字符串中的所有变位词

题目描述

给定两个字符串 s 和 p,找到 s 中所有 p 的 变位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。

变位词 指字母相同,但排列不同的字符串

剑指 Offer II 015. 字符串中的所有变位词 - 力扣(LeetCode) (leetcode-cn.com)

题解

固定窗口双指针

上一道题改一下:

def findAnagrams(self, s: str, p: str) -> List[int]:
    arr1, arr2, lg = [0] * 26, [0] * 26, len(p)
    ret = []
    if lg > len(s):
        return []
    # 维护一个arr2, 使它在s2上往右滑直到碰到尾巴
    for i in range(lg):
        arr1[ord(p[i]) - ord('a')] += 1
        arr2[ord(s[i]) - ord('a')] += 1

    for j in range(lg, len(s)+1):
        if arr1 == arr2:
            ret.append(j-lg)
        if j<len(s):
            arr2[ord(s[j - lg]) - ord('a')] -= 1
            arr2[ord(s[j]) - ord('a')] += 1
    return ret

剑指 Offer II 016. 不含重复字符的最长子字符串

题目描述

给定一个字符串 s ,请你找出其中不含有重复字符的 最长连续子字符串 的长度。

剑指 Offer II 016. 不含重复字符的最长子字符串 - 力扣(LeetCode) (leetcode-cn.com)

题解

双指针+hash表

双指针,维护一个字典,key为字符,value为上一次遇到该字符的位置

def lengthOfLongestSubstring(self, s: str) -> int:
    # 双指针+hash表
    dic, res, i = {}, 0, -1
    for j in range(len(s)):
        if s[j] in dic:
            i = max(dic[s[j]], i) # 更新左指针 i
        dic[s[j]] = j # 哈希表记录
        res = max(res, j - i) # 更新结果
    return res

动态规划dp

动态规划列表 dp ,dp[j] 即 代表以字符 s[j] 为结尾的 “最长不重复子字符串” 的长度。所以只需要一次for循环

def lengthOfLongestSubstring(self, s: str) -> int:
        #动态规划+hash表 设动态规划列表 dp ,dp[j]    即 代表以字符 s[j] 为结尾的 “最长不重复子字符串” 的长度。但是由于只要max, 只需用一个变量做动态规划额外的空间
        dic = {} #记录s[i] 的 i  s[i] 为距离s[j]最近的那个字符  即s[i]=s[j] i<j
        
        tmp = 0
        res = 0
        for j in range(len(s)):
            i = dic.get(s[j],-1) #获取索引i  参数-1表示找不到就返回-1
            dic[s[j]] = j
            if tmp < j-i:  #tmp中存的是dp[j-1]  即当前(shangyige)字符的dp[值]
                tmp = tmp+1
            else:
                tmp = j-i
            res = max(res,tmp)  # max(dp[j-1],dp[j])
        return res

 

剑指 Offer II 017. 含有所有字符的最短字符串

题目描述

给定两个字符串 s 和 t 。返回 s 中包含 t 的所有字符的最短子字符串。如果 s 中不存在符合条件的子字符串,则返回空字符串 "" 。

如果 s 中存在多个符合条件的子字符串,返回任意一个。

 

注意: 对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/M1oyTv

题解

暴力搜(当然超出时间限制,但其实还可以)

def minWindow(self, s: str, t: str) -> str:
    #集合 
    # t中所有字符都要在
    lg,n = len(t),len(s)
    set_t = set(t)
    set_tmp = {}
    ret = n+1
    res = ""
    for end in range(n):
        start = 0
        while start <=end:
            tempset = set(s[start:end+1])
            flag = True
            for _ in set_t:
                if s[start:end+1].count(_) < t.count(_):
                    flag = False
            if flag and tempset.intersection(set_t) == set_t:
                if end-start+1<ret:
                    ret = end-start+1
                    res = s[start:end+1]
            start+=1
    return res

双指针+hash表


 

Counter()函数
Counter()是collections里面的一个类,作用是计算出字符串或者列表等中不同元素出现的个数,返回值可以理解为一个字典,所以对传回来的统计结果的操作都可以当作对字典的操作
Note: 字符串置函数count(),只能统计字符串中某个元素出现的个数。
from collections import Counter
t="xbnpukocakzqzuhdlxoga"
hashmap = Counter(t)
print(hashmap)

output:
Counter({'x': 2, 'u': 2, 'k': 2, 'o': 2, 'a': 2, 'z': 2, 'b': 1, 'n': 1, 'p': 1, 'c': 1, 'q': 1, 'h': 1, 'd': 1, 'l': 1, 'g': 1})

 


 

  • 设t有n个字符,则挑出来的子字符串至少应有n个字符
  • 使用hashmap来保存窗口还需要的字符
 
def minWindow(self, s: str, t: str) -> str:
    # 双指针+hash表
    m, n = len(s), len(t)
    if m < n: return ""
    hashmap = Counter(t)
    count = n #这个变量很关键,保证含有字符串t中的所有字符
    ans = ""
    tmpLen = m + 1
    i, j = 0, 0
    while j < m:
        if s[j] in hashmap:
            if hashmap[s[j]] > 0:
                count -= 1
            hashmap[s[j]] -= 1
        while count == 0:
            if j - i + 1 < tmpLen:
                tmpLen = j - i + 1
                ans = s[i:j+1]
            if s[i] in hashmap:
                if hashmap[s[i]] >= 0:
                    count += 1
                hashmap[s[i]] += 1
            #当把左指针向右移动时,要对应更新hashmap和count
            i += 1
        j += 1
    return ans

 

剑指 Offer II 018. 有效的回文

题目描述

给定一个字符串 s ,验证 s 是否是 回文串 ,只考虑字母和数字字符,可以忽略字母的大小写。

本题中,将空字符串定义为有效的 回文串 。

剑指 Offer II 018. 有效的回文 - 力扣(LeetCode) (leetcode-cn.com)

题解

双指针+正则表达式

  • 忽略大小写:转换成大写or小写
  • 只考虑字母和数字:使用正则表达式去除多余字符

 

正则表达式re

Day 9:Python 字符串和正则介绍总结 - PiaYie - 博客园 (cnblogs.com)

匹配 a-z0-9

str1="".join(re.findall(r'[a-z0-9]*',s.lower()))

匹不是中文、大小写、数字的其他字符

cop = re.compile("[^\u4e00-\u9fa5^a-z^A-Z^0-9]")

 


 

def isPalindrome(self, s: str) -> bool:
    if not s:
        return true
    # 是否回文,
    # 忽略大小写
    str1 = s.lower()
    # 只保留字母+数字
    cop = re.compile("[^\u4e00-\u9fa5^a-z^A-Z^0-9]") # 匹配不是中文、大小写、数字的其他字符
    str1 = cop.sub('', str1)
    i,j = 0,len(str1)-1
    ret = True
    while i<=j:
        if str1[i]!=str1[j]:
            return False
        i+=1
        j-=1
    return ret   

 

剑指 Offer II 019. 最多删除一个字符得到回文

题目描述

给定一个非空字符串 s,请判断如果 最多 从字符串中删除一个字符能否得到一个回文字符串。

剑指 Offer II 019. 最多删除一个字符得到回文 - 力扣(LeetCode) (leetcode-cn.com)

题解

利用上题判断回文的方法

暴力搜

遍历一次数组,copy删除每一个字符后的字符串再做回文check,超出时间限制mmp

import re
s ="xinoiqersxmiddtxzemwdklzwforualxmfkgvjzcqgmamrijpvmuyskclmyawsfjcwmgsvumvuklkuozziygyoyodvlhbdxgcwwvkgzgjugtscsawowvbzwutfuipozjjpwennhwxzbaswiqxrqehummyoekvfwsozusaibibfygbkxwtrdmgfhvrgwnniuvkqwneknoxomubaukvpgkjfpftxjfvytngastmzgzsxjrqcutqbnzfnhdypllfhyjntptmudayqgkfzfsitzpvxohndlzwcbyivkkazhlhfatefiaazwxishxusjztkluxkqtunamsbtkryipoaebfshocwrhukpoknqbjmojeutqwivxwmvwshvqkbxryyirpcdxufqruhodtfyobuvpvdubmahbgdudwzjpfoeyieihvfxrlatikzthubzuenjlqcnjdyloimtolntfxhgjpnbahpvaravtfvkhzvkynbmljyqnjydeljtmwmxkqxmnywmzrcrkmqgpykhjeyxybtdyktsymaojbpzyhijdlmhsubsshhacagqzcodfcyvbbfyearfmheahmprcdllgcnblwuutghqoixevurqgpvsaxueqzcsdhxoiigpjzqhjkevnzcnaerpdoqzyclkpidqghoanqaccfudqggloeclppyjidinkdydfutkivxdodorxfgzjaawngeycuhfbctmojuvtmraudilnlvqrjuxugdmwxnocwgmvcyuxegkrwxnmxubiwphjbigcpkolllcghyicsaaccfifyjjaqanqatzqbwbfgqzrmmtgrgupdsnakfolnclcyzfvwtrtyedltlsxdefpvefilecjvnrewmhodnzsgaxpeekgrtgddqowynyhsbcraroacrmcdyqhhichtyyitlfxsfuyiqmvunaraeghkuqtqkztvnrjicfbenqlffdohkcceyllircklrdeclqtdkhllci"
def validPalindrome( s: str) -> bool:
    def isPalindrome(str1: str) -> bool:
        if not s:
            return True
        i,j = 0,len(str1)-1
        while i<=j:
            if str1[i]!=str1[j]:
                return False
            i+=1
            j-=1
        return True 
    str1 = s.lower()
        # 只保留字母+数字
    cop = re.compile("[^\u4e00-\u9fa5^a-z^A-Z^0-9]") # 匹配不是中文、大小写、数字的其他字符
    str1 = cop.sub('', str1)
    if isPalindrome(str1):
        return True
    for i in range(len(str1)):
        temp = str1[:i]+str1[i+1:]
        if isPalindrome(temp):
            return True
    return False

print(validPalindrome(s))

回文性质+技巧

 

 只要做三次check判断就可以了

def validPalindrome(self, s):
    # check函数用来确定两个字符是不是相等
    def check(l, r):
        while l <= r:
            if s[l] != s[r]:
                break
            l += 1
            r -= 1
        return l, r

    mid = len(s) // 2
    left, right = check(0, len(s) - 1)
    if left > mid:
        return True
    return check(left + 1, right)[0] > mid or check(left, right - 1)[0] == mid

 

剑指 Offer II 020. 回文子字符串的个数

题目描述

给定一个字符串 s ,请计算这个字符串中有多少个回文子字符串。

具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。

剑指 Offer II 020. 回文子字符串的个数 - 力扣(LeetCode) (leetcode-cn.com)

题解

中心扩散

def countSubstrings(self, s: str) -> int:
    n = len(s)
    #最多能向外扩散几步
    def check(l,r):
        count = 0
        while l >= 0 and r<n and s[l] == s[r]:
            count+=1
            l -= 1
            r += 1
        return count
    
    ret = 0
    for i in range(n):
        # 字串奇数长 中心为s[i]
        odd = check(i, i)
        # 字串偶数长 中心为s[i]s[i+1]这个也要比一比
        even = check(i, i + 1)
        ret += odd
        ret += even
    return ret

动态规划

posted @ 2021-12-26 10:45  PiaYie  阅读(68)  评论(0编辑  收藏  举报