[Leetcode]5917. 同源字符串检测

13 篇文章 1 订阅

【题目描述如下】

原字符串由小写字母组成,可以按下述步骤编码:

任意将其 分割 为由若干 非空 子字符串组成的一个 序列 。
任意选择序列中的一些元素(也可能不选择),然后将这些元素替换为元素各自的长度(作为一个数字型的字符串)。
重新 顺次连接 序列,得到编码后的字符串。
例如,编码 "abcdefghijklmnop" 的一种方法可以描述为:

将原字符串分割得到一个序列:["ab", "cdefghijklmn", "o", "p"] 。
选出其中第二个和第三个元素并分别替换为它们自身的长度。序列变为 ["ab", "12", "1", "p"] 。
重新顺次连接序列中的元素,得到编码后的字符串:"ab121p" 。
给你两个编码后的字符串 s1 和 s2 ,由小写英文字母和数字 1-9 组成。如果存在能够同时编码得到 s1 和 s2 原字符串,返回 true ;否则,返回 false。

注意:生成的测试用例满足 s1 和 s2 中连续数字数不超过 3 。

示例 1:

输入:s1 = "internationalization", s2 = "i18n"
输出:true
解释:"internationalization" 可以作为原字符串
- "internationalization" 
  -> 分割:      ["internationalization"]
  -> 不替换任何元素
  -> 连接:      "internationalization",得到 s1
- "internationalization"
  -> 分割:      ["i", "nternationalizatio", "n"]
  -> 替换:      ["i", "18",                 "n"]
  -> 连接:      "i18n",得到 s2
示例 2:

输入:s1 = "l123e", s2 = "44"
输出:true
解释:"leetcode" 可以作为原字符串
- "leetcode" 
  -> 分割:       ["l", "e", "et", "cod", "e"]
  -> 替换:       ["l", "1", "2",  "3",   "e"]
  -> 连接:       "l123e",得到 s1
- "leetcode" 
  -> 分割:       ["leet", "code"]
  -> 替换:       ["4",    "4"]
  -> 连接:       "44",得到 s2
示例 3:

输入:s1 = "a5b", s2 = "c5b"
输出:false
解释:不存在这样的原字符串
- 编码为 s1 的字符串必须以字母 'a' 开头
- 编码为 s2 的字符串必须以字母 'c' 开头
示例 4:

输入:s1 = "112s", s2 = "g841"
输出:true
解释:"gaaaaaaaaaaaas" 可以作为原字符串
- "gaaaaaaaaaaaas"
  -> 分割:       ["g", "aaaaaaaaaaaa", "s"]
  -> 替换:       ["1", "12",           "s"]
  -> 连接:       "112s",得到 s1
- "gaaaaaaaaaaaas"
  -> 分割:       ["g", "aaaaaaaa", "aaaa", "s"]
  -> 替换:       ["g", "8",        "4",    "1"]
  -> 连接         "g841",得到 s2
示例 5:

输入:s1 = "ab", s2 = "a2"
输出:false
解释:不存在这样的原字符串
- 编码为 s1 的字符串由两个字母组成
- 编码为 s2 的字符串由三个字母组成
 

提示:

1 <= s1.length, s2.length <= 40
s1 和 s2 仅由数字 1-9 和小写英文字母组成
s1 和 s2 中连续数字数不超过 3
通过次数195提交次数1,093

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/check-if-an-original-string-exists-given-two-encoded-strings
【题目分析】

    该问题求解可以分解为子问题的求解,属于典型的动态规划问题;

    定义d = dp[i][j] 为s1前i个字符与s2前j个字符匹配时,两者的字符个数差值,即长度差值;

    所谓匹配,即为两个字符串均可由原字符串通过一定的方法编码得到;

    分析可能匹配的几种情况,存在下面的转移方程:

    1、如果 s1[i, p]为数字, 则dp[i][j] -> dp[p][j], dp[p][j] = d + s1[i,p]; p- i+1 最大为3;

    注意:数字可以分开,也可以合并,所以对应dp[p][j]也有好几种取值;

    2、如果 s2[j, q]为数字,则dp[i][j] -> dp[i][q], dp[i][q] = d - s2[j, q]; q-j+1 最大为3;

    3、如果 s1[i]为字符,则仅当 d < 0时,才有可能匹配,此时, dp[i+1][j] = d + 1;

    4、如果 s2[j]为字符,则仅当 d > 0时,才有可能匹配,此时, dp[i][j+1] = d - 1;

    5、如果 s1[i]为字符,s2[j]为字符, 若d = 0,则只有两者相等时才有可能匹配,此时dp[i+1][j+1]=d=0;

【代码如下】

class Solution {
    bool IsDig(char ch)
    {
        return ch >= '0' && ch <= '9';
    }
public:
    /* 
    该问题求解可以分解为子问题的求解,属于典型的动态规划问题;
    定义d = dp[i][j] 为s1前i个字符与s2前j个字符匹配时,两者的字符个数差值,即长度差值;
    所谓匹配,即为两个字符串均可由原字符串通过一定的方法编码得到;
    分析可能匹配的几种情况,存在下面的转移方程:
    如果 s1[i, p]为数字, 则dp[i][j] -> dp[p][j], dp[p][j] = d + s1[i,p]; p- i+1 最大为3;
    注意:数字可以分开,也可以合并,所以对应dp[p][j]也有好几种取值;
    如果 s2[j, q]为数字,则dp[i][j] -> dp[i][q], dp[i][q] = d - s2[j, q]; q-j+1 最大为3;
    如果 s1[i]为字符,则仅当 d < 0时,才有可能匹配,此时, dp[i+1][j] = d + 1;
    如果 s2[j]为字符,则仅当 d > 0时,才有可能匹配,此时, dp[i][j+1] = d - 1;
    如果 s1[i]为字符,s2[j]为字符, 若d = 0,则只有两者相等时才有可能匹配,此时dp[i+1][j+1]=d=0;
    
    */
    bool possiblyEquals(string s1, string s2) {
        int n = s1.size(), m = s2.size();
        vector<vector<unordered_set<int>>> dp(n + 1, vector<unordered_set<int>>(m + 1));
        dp[0][0].emplace(0);
                
        for (int i = 0; i <= n; ++i) {
            for (int j = 0; j <= m; ++j) {
                for (int delta : dp[i][j]) {
                    int num = 0;
                    for (int p = i; p < min(i + 3, n); ++p) {
                        if (IsDig(s1[p])) {
                            num = num * 10 + s1[p] - '0';
                            dp[p + 1][j].emplace(delta + num);
                        } else {
                            break;
                        }
                    }
                    
                    num = 0;
                    for (int q = j; q < min(j + 3, m); ++q) {
                        if (IsDig(s2[q])) {
                            num = num * 10 + s2[q] - '0';
                            dp[i][q + 1].emplace(delta - num);
                        } else {
                            break;
                        }
                    }
                    
                    if (i < n && delta < 0 && !IsDig(s1[i])) 
                        dp[i + 1][j].emplace(delta + 1);
                            
                    if (j < m && delta > 0 && !IsDig(s2[j])) 
                        dp[i][j + 1].emplace(delta - 1);
                            
                    if (i < n && j < m && delta == 0 && s1[i] == s2[j])
                        dp[i + 1][j + 1].emplace(0);
                }
            }
        }
        
        return dp[n][m].count(0); 
    }
};

    

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值