【Leetcode】727. Minimum Window Subsequence

题目地址:

https://leetcode.com/problems/minimum-window-subsequence/

给定两个字符串 s 1 s_1 s1 s 2 s_2 s2,求 s 1 s_1 s1中长度最短的子串,使 s 2 s_2 s2是它的子序列,返回该子串。若有若干长度相同的,则返回起始位置最左边的那个。

思路是序列自动机(动态规划解法参考https://blog.csdn.net/qq_46105170/article/details/119351720)。先构造 s 1 s_1 s1的序列自动机,然后枚举 s 2 [ 0 ] s_2[0] s2[0] s 1 s_1 s1中的位置(找 s 2 [ 0 ] s_2[0] s2[0]的位置本身可以通过序列自动机来找),对于每个位置,都能通过序列自动机找到最短的以 s 2 s_2 s2为子序列的子串的终点位置(跳转 s 2 s_2 s2长度那么多次即可),如果能走到终点位置(即序列自动机判定子序列成功),则尝试更新答案,否则继续找 s 2 [ 0 ] s_2[0] s2[0] s 1 s_1 s1中出现的下一个位置继续枚举。代码如下:

public class Solution {
    public String minWindow(String s1, String s2) {
        int[][] dfa = buildDFA(s1);
        String res = "";
        // idx表示从哪个位置开始向后找s2[0],end表示找到的子序列的末尾在s1中的位置,下标都从1开始
        int idx = 0, end = 0;
        // 如果还能在idx之后找到s2[0],则开始枚举该位置起步能找到的子序列
        while ((end = compute(idx, dfa, s2)) != 0) {
        	// 子串开始位置是s2[0]在s1里出现的位置,下标从1开始
            int begin = dfa[idx][s2.charAt(0) - 'a'];
            // 尝试更新答案,左端点的真实下标是begin - 1,右端点的真实下标是end - 1
            if (res.isEmpty() || end - (begin - 1) < res.length()) {
                res = s1.substring(begin - 1, end);
            }
            
            // 把枚举的位置向后移动
            idx = begin;
        }
        
        return res;
    }
    
    // 从idx的位置开始找以s为子序列的最短子串的右端点,下标都从1开始
    private int compute(int idx, int[][] dfa, String s) {
        for (int i = 0; i < s.length(); i++) {
            int x = s.charAt(i) - 'a';
            // s[i]找不到了,说明不存在这样的子串,返回0
            if (dfa[idx][x] == 0) {
                return 0;
            }
            
            // 否则向后跳
            idx = dfa[idx][x];
        }
        
        // 返回最后一个字符出现的位置,下标从1开始
        return idx;
    }
    
    // 构造序列自动机
    private int[][] buildDFA(String s) {
        int[][] dfa = new int[s.length() + 1][26];
        for (int i = s.length() - 1; i >= 0; i--) {
            for (int j = 0; j < 26; j++) {
                dfa[i][j] = dfa[i + 1][j];
            }
            
            dfa[i][s.charAt(i) - 'a'] = i + 1;
        }
        
        return dfa;
    }
}

时间复杂度 O ( l s 1 l s 2 ) O(l_{s_1}l_{s_2}) O(ls1ls2),空间 O ( l s 1 ) O(l_{s_1}) O(ls1)

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值