包含字串的起始位置问题

150 阅读2分钟

「这是我参与2022首次更文挑战的第25天,活动详情查看:2022首次更文挑战」。

给定长度为m的字符串aim,以及一个长度为n的字符串str, 问能否在str中找到一个长度为m的连续子串, 使得这个子串刚好由aim的m个字符组成,顺序无所谓, 返回任意满足条件的一个子串的起始位置,未找到返回-1

LeetCode

一、分析

str2:acbca (a:2,c:2,b:1),5个字符

str1:ccaab (a:2,c:2,b:1),5个字符

顺序无所谓,种类必须一样,每种字符的个数也得一样,叫做变形词

如果存在str1的子串是str2的变形词的话,返回开始位置

遍历str2,比如ccaba,得出一张表,两个变量,(a=2,b=1,c=2,all=5)

维持一个长度为5个窗口

str1:cccbabacbca......

str1定义一个欠账表,窗口再扩的时候,c进来**|c|,还款一个c,c减减,(a=2,b=1,c=1,all=4),窗口再扩一个位置,c进来|cc|,还款一个c,c减减,(a=2,b=1,c=0,all=3),窗口再扩一个位置,c进来|ccc|,还款一个c,c减减,(a=2,b=1,c=-1,all=3)** 说明不是一次有效的还款,all不动还是all=3,窗口再扩的时候,b进来**|cccb|,还款一个b,b减减,(a=2,b=0,c=-1,all=2),窗口再扩的时候,a进来|cccba|,还款一个a,a减减,(a=1,b=0,c=-1,all=1)此时窗口形成了,长度为5|cccba|不是str2的变形词,all不等于0,有效的还款all !=0 ,说明一定有对不上的地方**,窗口再扩的时候,b进来,c出去c|ccbab|,还款一个b,b减减,出去一个c,c加加,(a=1,b=-1,c=0,all=1),依次类推......

count:欠债表

0 1 2 3 4 5 6,窗口长度为3

0~2,3位置上看是否有效

1~3,4位置上看是否有效

2~3,5位置上看是否有效

3~5,6位置上看是否有效

4~6,结束了,还需要看是否有效

二、实现

public static int containExactly3(String s1, String s2) {
    if (s1 == null || s2 == null || s1.length() < s2.length()) {
        return -1;
    }
    char[] str2 = s2.toCharArray();
    int M = str2.length;
    // 欠债表
    int[] count = new int[256];
    for (int i = 0; i < M; i++) {
        count[str2[i]]++;
    }
    int all = M;
    char[] str1 = s1.toCharArray();
    int R = 0;
    // 0~M-1
    for (; R < M; R++) { // 最早的M个字符,让其窗口初步形成
        if (count[str1[R]]-- > 0) {
            all--;
        }
    }
    // 窗口初步形成了,并没有判断有效无效,决定下一个位置一上来判断
    // 接下来的过程,窗口右进一个,左吐一个
    for (; R < str1.length; R++) {
        if (all == 0) { // R-1
            return R - M;
        }
        // 右进一个减减
        if (count[str1[R]]-- > 0) {
            all--;
        }
        // 左吐一个加加
        if (count[str1[R - M]]++ >= 0) {
            all++;
        }
    }
    return all == 0 ? R - M : -1;
}

三、总结

字串一定是连续的,子序列不一定是连续的。

滑动窗口