两个字符串

目录

(字符串的匹配)

 

一、题目:正则化表达式匹配【含通配符】

给定一个正则字符串p,一个字符串s。要求验证s和p是否能匹配。

特别的,正则字符串中仅由两个特殊字符:'.'表示任意的单个字符,'*'表示其前方紧邻元素连续出现0个或者更多个。要求匹配需要覆盖整个输入字符串,而不是部分的匹配。

s 可能为空,且只包含从 a-z 的小写字母。
p 可能为空,且只包含从 a-z 的小写字母,以及字符 . 和 *。

Some examples: isMatch("aa","a") → false
isMatch("aa","aa") → true
isMatch("aaa","aa") → false
isMatch("aa", "a*") → true
isMatch("aa", ".*") → true
isMatch("ab", ".*") → true
isMatch("aab", "c*a*b") → true

思路:动态规划:

状态转移数组 f [i] [j] 表示利用 p 的前 i 个字符匹配 s 的前 j 个字符的匹配结果(成功为true,失败为false)。

边界:

    • dp[0][0] = True,s和p都是空格。
    • i = 0,只有p为这种情况 p = ‘a*b*c*'才能True,别的都为False。

        for i in range(1,len(p)+1):           

if p[i-1] == '*': if i >= 2:

dp[0][i] = dp[0][i-2]

    • j = 0,全部为False。即p为空,都False。

非边界:

如果s[i] == p[j] 或者 p[j] == '.':【如果s的最后一位和p的最后一位相同,则只需要判断前面的】

dp[i][j] = dp[i - 1][j - 1]

如果p[j] == '*':【dp[i][j-2]:*前面一位为0个即可,Sx~P】【dp[i][j-1]:*前面一位为1个,Sx~Pz】【dp[i-1][j]:*匹配x(x==z或者z=='.'), S~Pzy】

  dp[i][j] = dp[i][j-2] || dp[i][j-1] || (dp[i-1][j] and (s[i-1]==p[j-2] or p[j-2]=='.'))

解释:

对于s和p,设各个最后一个字符为x, y,p的倒数第二字符为z,除此外前面字符设为S,P,则:

s = Sx

p = Pzy

    • 如果x == y或y == '.',则如果S和Pz匹配,则s和p匹配,因为最后两字字母是匹配的。这就缩减了问题规模。
    • 而对于y ==  '*'的情况,需要考虑z:

    如果x != z,则只有在s和P匹配的情况下,s和p才匹配。

    如果x == z,设匹配符号为~吧,方便,则如果S~PzySx~PSx~Pz,【S~P,S~Pz也匹配】都可得出s和p匹配。

 代码:

复制代码
    def isMatch(self, s, p):
        """
        :type s: str
        :type p: str
        :rtype: bool
        """
        dp = [[False] * (len(p) + 1) for i in range(len(s) + 1)]
        dp[0][0] = True

        for i in range(1,len(p)+1):
            if p[i-1] == '*':
                if i >= 2:
                    dp[0][i] = dp[0][i-2]
        for i in range(1,len(s)+1):
            for j in range(1,len(p)+1):
                if p[j-1]=='.' or s[i-1] == p[j-1]:
                    dp[i][j] = dp[i-1][j-1]
                elif p[j-1]=='*':
                    dp[i][j] = dp[i][j-2] or dp[i][j-1] or (dp[i-1][j] and (s[i-1]==p[j-2] or p[j-2]=='.'))
    return dp[len(s)][len(p)]
复制代码

 

一、题目:同构字符串

给定两个字符串 和 t,判断它们是否是同构的。

如果 中的字符可以被替换得到 ,那么这两个字符串是同构的。

所有出现的字符都必须用另一个字符替换,同时保留字符的顺序。两个字符不能映射到同一个字符上,但字符可以映射自己本身。

示例 1:

输入: s = "egg", t = "add"
输出: true

示例 2:

输入: s = "foo", t = "bar"
输出: false

示例 3:

输入: s = "paper", t = "title"
输出: true

说明:
你可以假设 t 具有相同的长度。

代码:

def isIsomorphic(self, s, t):
    return len(set(zip(s,t))) == len(set(s)) == len(set(t))

、题目:单词模式:

给定一种 pattern(模式) 和一个字符串 str ,判断 str 是否遵循相同的模式。

这里的遵循指完全匹配,例如, pattern 里的每个字母和字符串 str 中的每个非空单词之间存在着双向连接的对应模式。

示例1:

输入: pattern = "abba", str = "dog cat cat dog"
输出: true

示例 2:

输入:pattern = "abba", str = "dog cat cat fish"
输出: false

示例 3:

输入: pattern = "aaaa", str = "dog cat cat dog"
输出: false

示例 4:

输入: pattern = "abba", str = "dog dog dog dog"
输出: false

说明:
你可以假设 pattern 只包含小写字母, str 包含了由单个空格分隔的小写字母。    

代码:

复制代码
    def wordPattern(self, pattern, str):
        """
        :type pattern: str
        :type str: str
        :rtype: bool
        """
        a = str.split(" ")
        if len(pattern) != len(a):
            return False
        return len(set(zip(a,pattern))) == len(set(pattern)) == len(set(a))
复制代码

 三、题目:查找和替换模式

你有一个单词列表 words 和一个模式  pattern,你想知道 words 中的哪些单词与模式匹配。

如果存在字母的排列 p ,使得将模式中的每个字母 x 替换为 p(x) 之后,我们就得到了所需的单词,那么单词与模式是匹配的。

(回想一下,字母的排列是从字母到字母的双射:每个字母映射到另一个字母,没有两个字母映射到同一个字母。)

返回 words 中与给定模式匹配的单词列表。

你可以按任何顺序返回答案。

示例:

输入:words = ["abc","deq","mee","aqq","dkd","ccc"], pattern = "abb"
输出:["mee","aqq"]
解释:
"mee" 与模式匹配,因为存在排列 {a -> m, b -> e, ...}。
"ccc" 与模式不匹配,因为 {a -> c, b -> c, ...} 不是排列。
因为 a 和 b 映射到同一个字母。

提示:

  • 1 <= words.length <= 50
  • 1 <= pattern.length = words[i].length <= 20

思路:时间O(n2)

对每个单词判断其与相应模式是否匹配,匹配则输出。

判断是否匹配函数strMatch,由于题目已知单词长度相同,只需判断在相同的位置,两个单词要有相同的相等或者不等关系即可。

代码:

复制代码
    def findAndReplacePattern(self, words, pattern):
        """
        :type words: List[str]
        :type pattern: str
        :rtype: List[str]
        """
        if not words or not pattern:
            return []
        def isSamePattern(s1,s2):
            for i in range(len(s1)):
                for j in range(i+1,len(s1)):
                    if s1[i] == s1[j] and s2[i] != s2[j]:
                        return False
                    elif s2[i] == s2[j] and s1[i] != s1[j]:
                        return False
            return True
                    
        i = 0
        while i < len(words):
            if isSamePattern(words[i],pattern):
                i += 1
            else:
                del words[i]
        return words
复制代码

一、重复子字符串

给定一个非空的字符串,判断它是否可以由它的一个子串重复多次构成。给定的字符串只含有小写英文字母,并且长度不超过10000。

示例 1:

输入: "abab"

输出: True

解释: 可由子字符串 "ab" 重复两次构成。

示例 2:

输入: "aba"

输出: False

示例 3:

输入: "abcabcabcabc"

输出: True

解释: 可由子字符串 "abc" 重复四次构成。 (或者子字符串 "abcabc" 重复两次构成。)

代码:

复制代码
    def repeatedSubstringPattern(self, s):
        """
        :type s: str
        :rtype: bool
        """
        if not s:
            return False
        ss = (s + s)[1:-1]
        return ss.find(s)!=-1
复制代码

、判断两个字符串是否互为旋转词

如果一个字符串str,把字符串str前面任意的部分挪到后面形成的字符串叫做str的旋转词。
如str="12345",str的旋转词有"12345"、"23451"、"34512"、"45123"、"51234"。
给定两个字符串a和b,请判断a和b是否互为旋转词。
举例:
a="cdab",b="abcd",返回true;
a="1ab2",b="ab12",返回false;
a="2ab1",b="ab12",返回true。
要求:
如果a和b长度不一样,那么a和b必然不互为旋转词,可以直接返回false。
当a和b长度一样,都为N时,要求解法的时间复杂度为O(N)。

思路:

将两个b拼接在一起赋值给c,查看c中是否包含字符串a,若包含,则返回true;否则返回false。

代码:

复制代码
def isRotation(a,b):
    if len(a) != len(b):
        return False
    c = b+b
    return (a in c)

a = 'cdab'
b = 'abcd'
isRotation(a,b)
        
复制代码

、题目:互为变形词

题目:给定两个字符串str1和str2,如果str1和str2中出现的字符种类一样且每种字符出现的次数也一样,则str1和str2互为变形词。
请实现函数判断两个字符串是否互为变形词。
举例:
str1="123", str2="231", 返回true;
str1="123", str2="2331",返回false。

思路:

 1. 首先比较两个字符串的长度,长度不同肯定是false。

 2. 如果长度相同,新建一个字典,用以存储每个字符出现次数。

 3. 遍历str1,在str1 中出现一次就加1,遍历str2,在str2 中出现一次就减1,最后遍历完str2没有出现负值,就返回true。

代码:

复制代码
from collections import Counter
def IsDeformation(str1,str2):
    if not str1 or not str2 or len(str1) != len(str2):
        return False
    countstr1 = Counter(str1)
    for s2 in str2:
        if s2 in countstr1:
            countstr1[s2] -= 1
            if countstr1[s2] < 0:
                return False
        else:
            return False
    return True
str1 = '1234'
str2 = '2313'
IsDeformation(str1,str2)
复制代码

一、数组中两个字符串的最小距离

思路:两个变量分别更新str1和str2的位置,res记录两个变量差的最小值。

代码:

复制代码
import sys
def minDistance(strs,str1,str2):
    if not strs or not str1 or not str2:
        return -1
    if str1 == str2:
        return 0
    last1 , last2 , res = -1 , -1 , sys.maxsize
    for i in range(len(strs)):
        if strs[i] == str1:
            if last2 != -1:
                res = min(res,i - last2)
            last1 = i
        if strs[i] == str2:
            if last1 != -1:
                res = min(res,i - last1)
            last2 = i
    return res if res != sys.maxsize else -1
strs = ['3','1','3','3','3','2','3','1']
str1 = '1'
str2 = '2'
minDistance(strs,str1,str2)
复制代码

二、题目:最小编辑代价

给定两个字符串str1和str2,再给定三个整数ic,dc,rc,分别代表插入、删除、替换一个字符的代价,返回将str1编辑成str2的最小代价。
举例:
str1="abc"   str2="adc"  ic=5    dc=3   rc=2,从"abc"编辑到"adc"把b替换成d代价最小,为2;
str1="abc"   str2="adc"  ic=5    dc=3   rc=10,从"abc"编辑到"adc",先删除b再插入d代价最小,为8;

思路:动态规划:时间O(M*N),空间O(M*N)

动态规划表:dp[i][j]表示str1[0......i-1]编辑成str2[0......j-1]的最小编辑代价,dp大小为(M+1)*(N+1)是为了从空串开始计算,即dp[0][0]表示空串编辑到空串的最小编辑代价。

状态方程:
  • 初始化:

dp[0][0] = 0

dp[0][j] = j * len(s2)

dp[i][0] = i * len(s1)

  • dp[i][j] = min( dp[i][j-1]+icdc+dp[i-1][j]dp[i-1][j-1] + rc 【如果str1[i-1]==str2[j-1],rc = rc,否则,rc = 0】)
1.dp[0][0]表示空串编辑成空串,故dp[0][0]=0;
2.求第一行dp[0][j],空串编辑成str2[0....j-1],则dp[0][j]=ic*j;
3.求第一列dp[i][0],str1[0......i-1]编辑成空串,则dp[i][0]=dc*i;
4.求dp[i][j],即str1[0....i-1]编辑成str2[0.....j-1],三种可能的途径:
<1>str1[0....i-1]先编辑成str2[0.....j-2],再由str2[0.....j-2]插入到str2[0.....j-1],即 dp[i][j-1] + ic;
<2>str1[0....i-1]先编辑成str1[0.....i-2],再由str1[0.....i-2]删除到str2[0.....j-1],即 dp[i-1][j] + dc;
<3>如果str1[i-1]==str2[j-1],则 dp[i][j] = dp[i-1][j-1];
       如果str1[i-1]!=str2[j-1],则 dp[i][j] = dp[i-1][j-1] + rc;
最后比较三种途径的最小值,即dp[i][j]的值。

代码:

复制代码
def mincost(s1,s2,ic,dc,rc):
    m , n = len(s1) , len(s2)
    if not s1:
        return n*ic
    if not s2:
        return m*dc
    dp = [[0] * (n+1) for i in range(m+1)]
    for i in range(1,n + 1):
        dp[0][i] = ic * i
    for j in range(1,m + 1):
        dp[j][0] = dc * j
    for i in range(1,m+1):
        for j in range(1,n+1):
            if s1[i-1] == s2[j-1]:
                dp[i][j] = dp[i-1][j-1]
            else:
                dp[i][j] = dp[i-1][j-1] + rc
            dp[i][j] = min( dp[i][j] , dp[i-1][j] + dc , dp[i][j-1] + ic)
    return dp[-1][-1]
s1 = 'ab12cd3'
s2 = 'abcdf'
ic , dc , rc = 5,3,2
mincost(s1,s2,ic,dc,rc)
复制代码

、题目:最长公共子序列:

给定两个字符串,求解这两个字符串的最长公共子序列(Longest Common Sequence)。比如字符串L:BDCABA;字符串S:ABCBDAB

则这两个字符串的最长公共子序列长度为4,最长公共子序列是:BCBA

思路:动态规划:时间O(n * m),空间O(n * m)

创建 DP数组C[i][j]:表示子字符串L【:i】和子字符串S【:j】的最长公共子序列个数。

状态方程:

个数代码:

复制代码
def LCS(L,S):
    if not L or not S:
        return ""
    dp = [[0] * (len(L)+1) for i in range(len(S)+1)]
    for i in range(len(S)+1):
        for j in range(len(L)+1):
            if i == 0 or j == 0:
                dp[i][j] = 0
            else:
                if L[j-1] == S[i-1]:
                    dp[i][j] = dp[i-1][j-1] + 1
                else:
                    dp[i][j] = max(dp[i-1][j],dp[i][j-1])
    return dp[-1][-1]
L = 'BDCABA'
S = 'ABCBDAB'
LCS(L,S)
复制代码

最长子序列代码:设置一个标志

复制代码
def LCS(L,S):
    if not L or not S:
        return ""
    res = ''
    dp = [[0] * (len(L)+1) for i in range(len(S)+1)]
    flag = [['left'] * (len(L)+1) for i in range(len(S)+1)]
    for i in range(len(S)+1):
        for j in range(len(L)+1):
            if i == 0 or j == 0:
                dp[i][j] = 0
                flag [i][j] = '0'
            else:
                if L[j-1] == S[i-1]:
                    dp[i][j] = dp[i-1][j-1] + 1
                    flag[i][j] = 'ok'
                else:
                    dp[i][j] = max(dp[i-1][j],dp[i][j-1])
                    flag[i][j] = 'up' if dp[i][j] == dp[i-1][j] else 'left'
    return dp[-1][-1],flag
def printres(flag,L,S):
    m = len(flag)
    n = len(flag[0])
    res = ''
    i , j = m-1 , n-1
    while i > 0 and j > 0:
        if flag[i][j] == 'ok':
            res += L[j-1]
            i -= 1
            j -= 1
        elif flag[i][j] == 'left':
            j -= 1
        elif flag[i][j] == 'up':
            i -= 1
    return res[::-1]            
L = 'BDCABA'
S = 'ABCBDAB'
num,flag = LCS(L,S)
res = printres(flag,L,S)
复制代码

、题目:最长公共子串

找出两个字符串最长连续的公共字符串,如两个母串cnblogs和belong,最长公共子串为lo

思路:动态规划:时间O(N*M),空间O(N*M)

将二维数组c[i][j]用来记录具有这样特点的子串——结尾同时也为子串x1x2⋯xi与y1y2⋯yj的结尾的长度。

代码:

复制代码
def lcs(s1,s2):
    if not s1 or not s2:
        return 0
    c = [[0] * len(s2) for i in range(len(s1))]
    result = 0
    for i in range(len(s1)):
        for j in range(len(s2)):
            if i == 0 or j == 0:
                c[i][j] = 0
            else:
                if s1[i-1] == s2[j-1]:
                    c[i][j] = c[i-1][j-1] + 1
                    result = max(c[i][j],result)
                else:
                    c[i][j] = 0
    return result
s1 = 'cnblogs'
s2 ='belong'
lcs(s1,s2)
复制代码

 

 

六、题目:字符串的字典序最长子序列

对于字符串a和b,如果移除字符串a中的一些字母(可以全部移除,也可以一个都不移除)就能够得到字符串b,则b为a的子序列。例如,‘heo'为'hello'的子序列,’le'不是。

对于给定的一个字符串s,请计算出s的字典序最大的子序列。

输入:输入包括一行,一个字符串s,字符串s长度length(1<=length<=50),s中每个字符串都是小写字母

输出:输出字符串,即s的字典序最大的子序列。

例如:‘test'输出:’tt'            'string' 输出 ‘trng'        'bike'输出'ke'

解法1:逆序排序,输出原来的索引值,保留上升的索引值,输出原来字符串对应索引值的子序列。

如:s='test',排序之后,s='ttse',索引应该是(0,3,2,1),0到3是升序,到2,1是降序,输出:(0,3)对应的(’tt')
再如:s='string',排序:s='tsrnig',索引(1,0,2,4,3,5)输出:(1,2,4,5)对应的(‘trng')
复制代码
def Sublist(s):
    '''
    type(s)=string
    '''
    value_s=list(s)
    key_s=range(len(s))
    dic=dict(zip(key_s,value_s))
#字典逆序排序
    sort_dic=sorted(dic.items(),key=lambda x:x[1],reverse=True)
    key=[]
    value=[]
    for item in sort_dic:
        key.append(item[0])
        value.append(item[1])
#获得逆序后的索引值
    cur=key[0]
    k=0
    for i in range(len(key)):
        if key[i-k]>=cur:
            cur=key[i-k]
        else:
            del key[i-k]
            k+=1
#返回对应索引的原字符串子序列
    res=[]
    for i in key:
        res.append(value_s[i])
    return str(res)
复制代码

解法2:逆向查找字符串,若前面的字符比后面一个大,则保留前面的字符,否则把它删除。

复制代码
def Subdic(s1):
    s=list(s1)
    s.reverse()
    k=0
    for i in range(len(s)-1):
        if s[i-k]>s[i+1-k]:
            del s[i+1-k]
            k+=1
    s.reverse()
    return str(s)
复制代码

、拼接所有字符串产生字典顺序最小的大写字符串

思路:排序本身时间O(NlogN)

假设两个字符分别是a,b。a和b拼起来的字符串表示为a.b,那么如果a.b的字典顺序小于b.a,就把a放在前面,否则把b放在前面。每两两字符之间都按照这个标准进行比较,以此标准排序后,最后串起来的结果就是正确答案。

如 ‘b' , 'ba',’b'和‘ba'排序后,’ba'应与'b'位置交换,‘ba’在前,‘b’在后。

代码:cmp_to_key是因为python3中没有cmp这种用法,取代的。

复制代码
def lowestString(chas):
    if chas == None or len(chas) == 0:
        return ""
    from functools import cmp_to_key
    chas = sorted(chas, key=cmp_to_key(lambda x,y: 1 if x+y > y+x else -1))
    return ''.join(chas)
chas = ['b','ba','abc','dba']
lowestString(chas)
复制代码

七、按照给定的字母序列对字符数组排序(python)

思路1采用字典存储规则:按照规则两两比较字符串

代码:

 1 from functools import cmp_to_key
 2 
 3 
 4 def camp(s1, s2, dic):
 5     m, n = len(s1), len(s2)
 6     i, j = 0, 0
 7     while i < m and j < n:
 8         tmp1, tmp2 = list(s1)[i], list(s2)[j]
 9         if tmp1 not in dic:
10             dic[tmp1] = -1
11         if tmp2 not in dic:
12             dic[tmp2] = -1
13         if dic[tmp1] > dic[tmp2]:
14             return 1
15         elif dic[tmp1] < dic[tmp2]:
16             return -1
17         else:
18             i += 1
19             j += 1
20     if i == len1:
21         return 1
22     else:
23         return -1
24 
25 
26 def compare(arr):
27     dic = {'d': 0, 'g': 1, 'e': 2, 'c': 3, 'f': 4, 'b': 5, 'o': 6, 'a': 7}
28     if len(arr) <= 1:
29         return arr
30     res = sorted(arr, key=cmp_to_key(lambda x, y: camp(x, y, dic)))
31 
32     return res
33 
34 
35 arr = ['bed', 'dog', 'dear', 'eye']
36 print(compare(arr))

、题目:词典中最长的子序列

给出一个字符串数组words组成的一本英语词典。从中找出最长的一个单词,该单词是由words词典中其他单词逐步添加一个字母组成。若其中有多个可行的答案,则返回答案中字典序最小的单词。

若无答案,则返回空字符串。

示例 1:

输入: 
words = ["w","wo","wor","worl", "world"]
输出: "world"
解释: 
单词"world"可由"w", "wo", "wor", 和 "worl"添加一个字母组成。

示例 2:

输入: 
words = ["a", "banana", "app", "appl", "ap", "apply", "apple"]
输出: "apple"
解释: 
"apply"和"apple"都能由词典中的单词组成。但是"apple"得字典序小于"apply"。

注意:

  • 所有输入的字符串都只包含小写字母。
  • words数组长度范围为[1,1000]
  • words[i]的长度范围为[1,30]

2、思路:

排序列表,从后往前遍历排序的列表,再遍历单词的子序列在不在列表中,若全在则把该单词加进结果列表中。最后找出结果列表中最长的单词,若多个单词长度一样,则选择字典序最前的。

代码1:

复制代码
    def longestWord(self, words):
        """
        :type words: List[str]
        :rtype: str
        """
        res=[]
        if not words:
            return ""
        else:
            newWords=sorted(words)          
            for i in range(0,len(newWords)):
                last_word=newWords[len(newWords)-1-i]
                while last_word in newWords:
                    last_word=last_word[:-1]
                if not last_word:
                    res.append(newWords[len(newWords)-1-i])                
            result=sorted(res,key=lambda x:len(x),reverse=True)
            flag=0
            for i in range(0,len(result)):
                if i==len(result)-1 or len(result[i])!=len(result[i+1]):
                    flag=i
                    break
                else:
                    continue
        return "" if not result else result[i]
复制代码

代码2:

复制代码
    def longestWord(self, words):
    ans = ""
    wordset = set(words)
    for word in words:
        if len(word) > len(ans) or len(word) == len(ans) and word < ans:
            if all(word[:k] in wordset for k in range(1, len(word))):
                ans = word

    return ans
复制代码

 题目:删除字符串重复的字母,返回字典序最小的一个

 思路:采用一个栈stack存字母、采用一个count字典数字符串的字符个数、采用一个visited字典表示该字母是否已经确定。

class Solution(object):
    def removeDuplicateLetters(self, s):
        """
        :type s: str
        :rtype: str
        """
        count = collections.Counter(s)
        stack = []
        visited = collections.defaultdict(bool)
        for c in s:
            count[c] -= 1
            if visited[c]:
                continue
            while stack and count[stack[-1]] and stack[-1] > c:
                visited[stack[-1]] = False
                stack.pop()
            visited[c] = True
            stack.append(c)
        return "".join(stack)

 

 
原文地址:https://www.cnblogs.com/Lee-yl/p/10466426.html