【LeetCode】Day67-判断子序列 & 编辑距离

题目1

392. 判断子序列【简单】

题解

暴力双指针

很简单,不多说了

class Solution {
    public boolean isSubsequence(String s, String t) {
        int m=s.length(),n=t.length(),i=0;
        for(int j=0;i<m && j<n;j++){
            if(s.charAt(i)==t.charAt(j))
                i++;
        }
        return i==m;
    }
}

时间复杂度: O ( m + n ) O(m+n) O(m+n)

空间复杂度: O ( 1 ) O(1) O(1)

动态规划

暴力其实就挺简单的了…而且复杂度也不错,但是官解中还给了动态规划的解法,刚开始不理解,后来看到评论里说的,假如有x条s要处理,此时双指针复杂度为 O ( x ( t l + s l ) ) O(x(tl+sl)) O(x(tl+sl)),但是动态规划复杂度只有 O ( x t l + s l ) O(xtl+sl) O(xtl+sl),恍然大悟

  1. 状态定义:dp[i][j] 表示字符串 t 中从位置 i 开始往后26个字母中每个字母第一次出现的位置
  2. 状态转移方程:如果 t[i] 就是 ‘a’+j,那么 dp[i][j]=i,否则 ‘a’+j 出现在位置 i+1 开始往后,即 dp[i][j]=dp[i+1][j]
    (1)t[i]==‘a’+j:dp[i][j] = i
    (2)t[i] !=‘a’+j:dp[i][j] = dp[i+1][j]
  3. 遍历顺序:因为遍历到 i 时,i+1必须有值,因此要从后向前遍历;j 从0到25
  4. 初始条件:dp[tl][i]=tl,表示t中不含该字母
class Solution {
    public boolean isSubsequence(String s, String t) {
        int sl=s.length(),tl=t.length();
        int dp[][]=new int[tl+1][26];
        //初始化dp,26个字母在t中的位置设置为tl,表示t中不存在该字母
        for(int i=0;i<26;i++)
            dp[tl][i]=tl;
        //26个字母在t中的位置
        for(int i=tl-1;i>=0;i--){
            for(int j=0;j<26;j++){
                if(t.charAt(i)=='a'+j)
                    dp[i][j]=i;
                else
                    dp[i][j]=dp[i+1][j];
            }
        }
        /*判断t中是否含有s*/
        int add=0;//t中字符下标
        //遍历s中字母i
        for(int i=0;i<sl;i++){
            //t中没有s[i]
            if(dp[add][s.charAt(i)-'a']==tl)
                return false;
            //t挪到t[s[i]]的下一个位置
            add=dp[add][s.charAt(i)-'a']+1;
        }
        return true;
    }
}

时间复杂度: O ( 26 ∗ t l + s l ) O(26*tl+sl) O(26tl+sl)

空间复杂度: O ( 26 ∗ t l ) O(26*tl) O(26tl)

题目2

72. 编辑距离【困难】

题解

又是一道经典的困难题…完全没思路…熟读背诵吧

首先分析操作类型,题目给出了三种:插入、删除和修改,那么对于两个单词A和B来说,一共有六种操作方式,但是会发现三个等式:

  1. 插入A=删除B(如 A=dog,B=doge)
  2. 删除A=插入B(如 A=doge,B=dog)
  3. 修改A=修改B(如 A=cat,B=bat)

所以可将六种操作方式简化为三种,插入A,插入B,修改A

再来看一下如何把问题转化为规模较小的子问题,假设A=horse,B=ros,

  1. 插入A:假设horse到ro的编辑距离为a,那么horse到ros的编辑距离不超过a+1,因为ro到ros只需要一步插入字符’s’(horse->ro->ros)
  2. 插入B:假设hors到ros的编辑距离为b,那么horse到ros的编辑距离不超过b+1,因为hors到horse只需要一步插入字符’e’(ros->hors->horse)
  3. 修改A:假设hors到ro的编辑距离为c,那么horse到ros的编辑距离不超过c+1,因为roe到ros只需要一步将’e’替换为’s’(horse->roe->ros)

所以从 horse 变成 ros 的编辑距离应该为 min(a + 1, b + 1, c + 1)

最后来讨论一下动态规划的4个重要问题

  1. 状态定义:dp[i][j] 表示 A 的前 i 个字母和 B 的前 j 个字母之间的编辑距离。
  2. 状态转移方程

    其中D[i][j-1] 为 A 的前 i 个字符和 B 的前 j - 1 个字符编辑距离的子问题。即对于B[j],我们在A的末尾添加了一个同样的字符 (->A和B最后一个字符相同,同时删去这个字符编辑距离也相同,对于A来说只是删除了添加的字符,因此还是i,对于B来说,删掉了末尾的字符,因此为j-1),则D[i][j] 最小可以为 D[i][j-1] + 1 (+1是在A末尾添加字符的那步操作)
    D[i-1][j] 为 A 的前 i - 1 个字符和 B 的前 j 个字符编辑距离的子问题。即对于A[i],我们在B的末尾添加了一个同样的字符,则D[i][j] 最小可以为 D[i-1][j] + 1;
    D[i-1][j-1] 为 A 前 i - 1 个字符和 B 的前 j - 1 个字符编辑距离的子问题。即对于 B [j],我们修改 A [i] 使它们相同,那么 D[i][j] 最小可以为 D[i-1][j-1] + 1。特别地,如果 A 的第 i 个字符和 B 的第 j 个字符原本就相同,那么我们实际上不需要进行修改操作。在这种情况下,D[i][j] 最小可以为 D[i-1][j-1]。

借用官解的图举个例子:
在这里插入图片描述

  1. 初始条件:dp[i][0]=i,表示对word1执行 i 次删除操作;dp[0][j]=j,表示对word2执行 j 次删除操作;
  2. 返回值:dp[n1-1][n2-1]
class Solution {
    public int minDistance(String word1, String word2) {
        int n1=word1.length(),n2=word2.length();
        //dp初始化
        int dp[][]=new int[n1+1][n2+1];
        for(int i=0;i<=n1;i++)
            dp[i][0]=i;//空字符到word1[0..i]只需插入i个字符
        for(int j=0;j<=n2;j++)
            dp[0][j]=j;
        for(int i=1;i<=n1;i++){
            for(int j=1;j<=n2;j++){
                int mod=dp[i-1][j-1];
                if(word1.charAt(i-1)==word2.charAt(j-1))
                    mod--;//最后一个字符相同,直接为dp[i-1][j-1],不用+1
                dp[i][j]=Math.min(dp[i-1][j],Math.min(dp[i][j-1],mod))+1;
            }
        }
        return dp[n1][n2];
    }
}

时间复杂度: O ( m n ) O(mn) O(mn)

空间复杂度: O ( m n ) O(mn) O(mn)

p.s 最近涉及字符串的动态规划简直是我的软肋,完全不会做

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值