字符串的最小包含子串

展开书签

问题

给定字符串str1,str2,获取字符串str1中包含str2的最小字符子串。

  • str1=“abcde”, str2=“bd” -> bcd
  • str1=“abcde”, str2=“cg” -> “”

思路

假定字符编码范围0~255

  • 创建一个size为256的整形数组charCount,用来保存字符串str2所有字符的出现次数
  • 整形变量match用来表示当前差几个字符未匹配
  • 将str1、str2分别转换为字符数组parentArray、subArray
  • 遍历subArray,保存str2每个字符出现的次数
  • 从左向右遍历parentArray,将每个字符对应的次数做–运算,之后如果字符的次数为零,表示匹配上一个字符,则match–
  • 若match等于0,表示str2中的所有字符均已匹配上,此时parentArray的字符位置则是最小子串的右边界限
  • 开始确定最小子串的左边界限,再次遍历parentArray,查找第一次出现次数为-1的字符位置,该位置则为左边界限
  • 获取最小子串str1.subString(左边界+1,右边界+1)

代码

**
 * 获取最小包含的子串
 * str1="some3gs", str2="og" -> ome3g
 */
public class GetMinContainsSubString {

    public static String getMinSubString(String str1, String str2) {
        if (str1 == null || str2 == null || str2.length() > str1.length()) {
            return "";
        }
        int[] charCount = new int[256];
        char[] parentArray = str1.toCharArray();
        char[] subArray = str2.toCharArray();
        for (int i=0;i<subArray.length;i++) {
            charCount[subArray[i]]++;
        }
        //最小子串右边界位置
        int i = 0;
        //最小子串左边界位置
        int j = 0;
        //需要匹配的总字符数
        int match = str2.length();
        String minSubString = "";
        while(i != parentArray.length) {
            char c = parentArray[i];
            charCount[c]--;
            //减1之后还大于等于0,表示之前存在该字符
            if (charCount[c] >= 0) {
                match--;
            }
            //此时已全部匹配,i则是右边界位置,此时还需压缩左边界位置
            if (match == 0) {
                while(charCount[parentArray[j++]] == -1 ) {
                    minSubString = str1.substring(j + 1, i + 1);
                    return minSubString;
                }
            }
            i++;
        }
        return minSubString;
    }

    public static void main(String[] args) {
        String str1 = "some45baby";
        String str2 = "m4a";
        System.out.println(getMinSubString(str1, str2));
    }
}