LeetCode刷题day030 (Jieky)

LeetCode第30题

/*
You are given a string, s, and a list of words, words, that are all of the same length. Find all starting indices of substring(s) in s that is a concatenation of each word in words exactly once and without any intervening characters.

Example 1:
Input:
  s = "barfoothefoobarman",
  words = ["foo","bar"]
Output: [0,9]
Explanation: Substrings starting at index 0 and 9 are "barfoor" and "foobar" respectively.
The output order does not matter, returning [9,0] is fine too.

Example 2:
Input:
  s = "wordgoodgoodgoodbestword",
  words = ["word","good","best","word"]
Output: []
*/
import java.util.*;
import java.lang.*;
public class SubstringConcatenationAllWords{
    public static void main(String[] args){
        String s = "barfoothebar";
        String[] words = {"bar","foo","the"};
        SubstringConcatenationAllWords scaw = new SubstringConcatenationAllWords();
        List<Integer> result = scaw.findSubstring(s,words);
        for(Integer temp : result){
            System.out.println(temp);
        }
    }
	// 遍历子串中所有的word,然后利用两个HashMap分别用于检查和计数,避免使用双层循环
	// 在以上的基础上,对子串匹配问题分三类情况:符合要求、不符合要求1(重复)、不符合要求2(异常)
	// 处理两种不符合要求情况的逻辑,最后做符合要求情况的判断(num == words.length)
	public List<Integer> findSubstring(String s, String[] words) {
        List<Integer> res = new ArrayList<Integer>();
        if (s == null || words.length == 0) return res;

        // words中所有的单词等长
        int wordLen = words[0].length();

        HashMap<String, Integer> allWords = new HashMap<String, Integer>();
        for (String w : words) {
            int value = allWords.getOrDefault(w, 0);
            allWords.put(w, value + 1);
        }

        //将所有移动分成 wordLen 类情况
        for (int j = 0; j < wordLen; j++) {
            HashMap<String, Integer> hasWords = new HashMap<String, Integer>();
            //每次移动一个单词长度
            int num = 0; 
            for (int i = j; i < s.length() - words.length * wordLen + 1; i = i + wordLen) {
                while (num < words.length) {
                    // 或子串第num个用于比较的单词
                    String word = s.substring(i + num * wordLen, i + (num + 1) * wordLen);

                    // word不在allWords中:情况1和情况3。word在allWords中:情况2.
                    if (allWords.containsKey(word)) {
                        num++;

                        // 更新hasWords中单词的个数
                        int value = hasWords.getOrDefault(word, 0);
                        hasWords.put(word, value + 1);

                        //出现情况三,遇到了符合的单词,但是次数超了
                        if (hasWords.get(word) > allWords.get(word)) {

                            //从子串头开始移除单词,直到hasWords中不符合单词次数限制的单词符合要求
                            int removeNum = 0;
                            while (hasWords.get(word) > allWords.get(word)) {
                                // 取出从子串头开始的第removeNum个单词
                                String tempWord = s.substring(i + removeNum * wordLen, i + (removeNum + 1) * wordLen);
                                // 更新hasWords中单词的个数
                                int v = hasWords.get(tempWord);
                                hasWords.put(tempWord, v - 1);
                                removeNum++;
                            }
                            // 更新符合要求单词个数,得到符合要求的num数
                            num = num - removeNum ; 
                            //这里依旧是考虑到了最外层的 for 循环,看情况二的解释。i指向出现问题的单词
                            i = i + (removeNum - 1) * wordLen; 
                            break;
                        }
                    //出现情况二,遇到了不匹配的单词,直接将 i 移动到该单词的后边(但其实这里
                    //只是移动到了出现问题单词的地方,因为最外层有 for 循环, i 还会移动一个单词
                    //然后刚好就移动到了单词后边)
                    } else {
                        hasWords.clear();
						// 这里的num是目前为止符合要求的num数。i指向出现问题的单词
                        i = i + num * wordLen;
						// 更新符合要求单词个数,得到符合要求的num数
                        num = 0;
                        break;
                    }   
                }

                //只要出现情况2和情况3,num的个数就会小于words.length
                //出现情况一,子串完全匹配,我们将上一个子串的第一个单词从 hasWords 中移除
                if (num == words.length) {
					// 此时没有出现问题的单纯,即i已经指向“出现问题”的单词
                    res.add(i);
                    String firstWord = s.substring(i, i + wordLen);
                    int v = hasWords.get(firstWord);
                    hasWords.put(firstWord, v - 1);
                    num = num - 1;
                }
				// 到这里i指向出现问题的单词,for循环的i = i + wordLen可以跳过出现问题的单纯
            }

        }
        return res;
    }
	
	// 遍历子串中所有的word,然后利用两个HashMap分别用于检查和计数,避免使用双层循环
	public List<Integer> findSubstring03(String s, String[] words) {
        List<Integer> ret = new ArrayList<Integer>();

        if (s == null || words.length == 0) return ret;
		
		// 将所有单词存储在HashMap中
		HashMap<String, Integer> allWords = new HashMap<String, Integer>();
		for (String w : words) {
			int value = allWords.getOrDefault(w, 0);
			allWords.put(w, value + 1);
		}
		
		// words中的单词是等长的
		int wordLen = words[0].length();

        // 循环遍历s,子串的初始索引要满足最小子串长度要求
        for (int i=0;i<s.length() - words.length * wordLen + 1;i++){
			
            HashMap<String, Integer> hasWords = new HashMap<String, Integer>();
			
			// 循环的次数为words的长度
			int num = 0;
			while(num < words.length){
				// 获得s子串对应的第num个单词
				String word = s.substring(i + num * wordLen, i + (num + 1) * wordLen);
				
				// 检查word是否在allWords中
				if (allWords.containsKey(word)) {
					// 更新hasWords对应单词的个数
					int value = hasWords.getOrDefault(word, 0);
					hasWords.put(word, value + 1);
					//判断hasWords中单词的个数是否超过allWords的限制
					if (hasWords.get(word) > allWords.get(word)) break;
				} else {
					// word不在allWords中,当前子串不和条件,跳出
					break;
				}
				// 只有当前word数符合要求mum才会++
				num++;
			}
			if (num == words.length) ret.add(i);
        }    
        return ret;
    }

    public List<Integer> findSubstring02(String s, String[] words) {
        List<Integer> ret = new ArrayList<Integer>();

        if (s == null || words.length == 0) return ret;


        // 循环遍历s
        for (int i=0;i<s.length();i++){
            List<Integer> temp = new ArrayList<Integer>();
            for (int j=0;j<words.length;j++){temp.add(j);}

            int cur = i;
            int count = 0;
            int ite = 0;
            int offset = i;
            while(ite < words.length){
                // 匹配words.length次,若不能完全匹配则跳出
                ite++;

                // 遍历words集合,寻找是否有符合要求的word
                for(Integer index:temp){
                    boolean ok = false;

                    // 判断当前单词是否可以接上
                    if (cur + words[index].length() -1 < s.length() &&
                            words[index].equals(s.substring(cur,cur + words[index].length()))){
                        ok = true;
                        cur += words[index].length();
                    }

                    if (ok){
                        // 当前单词满足要求count++,移除单词索引
                        count++;
                        // 更新再次比较的起始位
                        offset += words[index].length();
                        temp.remove(index);
                        break;
                    }else{
                        // 当前单词不符合则将比较索引归位
                        cur = offset;
                    }
                }
				
				// 第一遍没有找到符合要去的单词则跳出匹配
				if (count < 1) break;

                if (count == words.length) ret.add(i);
            }
        }
        return ret;
    }

    public List<Integer> findSubstring01(String s, String[] words) {
        List<Integer> ret = new ArrayList<Integer>();

        if (s == null || words.length == 0) return ret;


        // 循环遍历s
        for (int i=0;i<s.length();i++){
            List<Integer> temp = new ArrayList<Integer>();
            for (int j=0;j<words.length;j++){temp.add(j);}

            int cur = i;
            int count = 0;
            int ite = 0;
            int offset = i;
            while(ite < words.length){
                // 匹配words.length次,若不能完全匹配则跳出
                ite++;

                // 遍历words集合,寻找是否有符合要求的word
                for(Integer index:temp){
                    boolean ok = false;

                    for(int k=0;k<words[index].length() && cur < s.length();k++,cur++){
                        if (words[index].charAt(k) != s.charAt(cur)) break;
                        if (k == words[index].length() - 1) ok = true;
                    }

                    if (ok){
                        // 当前单词满足要求count++,移除单词索引
                        count++;
                        offset += words[index].length();
                        temp.remove(index);
                        break;
                    }else{
                        // 当前单词不符合则将比较索引归位
                        cur = offset;
                    }
                }

                if (count == words.length) ret.add(i);
            }
        }
        return ret;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值