题目链接:
https://leetcode.com/problems/substring-with-concatenation-of-all-words/
参考链接:
http://www.w2bc.com/Article/14500
思路:
题目大意为,给定一个字符串S和一个字符串数组L,L中的各字符串长度均相等。找出S中所有的子串,这些子串恰好包含L中所有字符各一次,返回子串启始位置。
解题步骤:
1.用map表示L,map中的Key为L中的各字符串,Value为该字符串出现次数
2.执行循环,用str表示S中的各字符串,cnt表示S中包含的L中字符串的个数。若map中key包含str且对应的value大于0(因为要恰好包含1次,),则cnt++,map中str对应的value--。若cnt==
L.length,则这一段满足题目要求。接着再次初始化map,继续执行循环
3.返回结果集
code:
package alivebao;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
/**
* You are given a string, S, and a list of words, L, that are all of the same
* length. Find all starting indices of substring(s) in S that is a
* concatenation of each word in L exactly once and without any intervening
* characters.
*
* For example, given: S: "barfoothefoobarman" L: ["foo", "bar"]
*
* You should return the indices: [0,9]. (order does not matter).
*
* @author Administrator
*
*/
public class Solution {
public static List<Integer> findSubstring(String S, String[] L) {
List<Integer> ans = new ArrayList<Integer>();
if (S.length() < 1 || L.length < 1)
return ans;
int len = L[0].length(); // 题目说L中每个单词长度一样
// 初始化HashMap,注意L中可能包含多个相同的字符串,所以用value表示个数
HashMap<String, Integer> map = new HashMap<String, Integer>();
for (int j = 0; j < L.length; j++) {
if (map.containsKey(L[j])) {
map.put(L[j], map.get(L[j]) + 1);
} else {
map.put(L[j], 1);
}
}
// i的范围很关键,如果直接到S.length()是会超时的
for (int i = 0; i <= S.length() - L.length * len; i++) {
int from = i;
String str = S.substring(from, from + len);
int cnt = 0;
while (map.containsKey(str) && map.get(str) > 0) {
map.put(str, map.get(str) - 1);
cnt++;
from += len;
if (from + len > S.length())
break; // 注意越界
str = S.substring(from, from + len);
}
// L中每个单词恰好出现一次,加入到结果集
if (cnt == L.length) {
ans.add(i);
}
// 为下一次初始化HashMap
if (cnt > 0) {
map.clear();
for (int j = 0; j < L.length; j++) {
if (map.containsKey(L[j])) {
map.put(L[j], map.get(L[j]) + 1);
} else {
map.put(L[j], 1);
}
}
}
}
return ans;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
String s = "barfoothefoobarman";
String[] L = { "foo", "bar" };
List<Integer> a = new ArrayList<Integer>();
a = findSubstring(s, L);
for (Integer t : a) {
System.out.println(t);
}
}
}
PS:感觉还有一种解法,类似Leetcode里的 Longest Substring Without Repeating Characterse,大概思路为先用移动窗口的办法求出字符串S中各不重复的子串,然后判断各子串中是否含有L
Longest Substring Without Repeating Characters的大致要求即为求出最长的不重复子串。解法:
Longest Substring Without Repeating Characters
package alivebao;
/**
* Given a string, find the length of the longest substring without repeating
* characters. For example, the longest substring without repeating letters for
* "abcabcbb" is "abc", which the length is 3. For "bbbbb" the longest substring
* is "b", with the length of 1.
*
* @author Administrator
*
*/
public class Solution {
public static int lengthOfLongestSubstring(String s) {
int maxLen = 0;
if (s == null || s.length() == 0)
return 0;
HashSet<Character> set = new HashSet<Character>();
int walker = 0;
int runner = 0;
while (runner < s.length()) {
if (set.contains(s.charAt(runner))) {
if (maxLen < runner - walker)
maxLen = runner - walker;
while (s.charAt(walker) != s.charAt(runner)) {
set.remove(s.charAt(walker));
walker++;
}
walker++;
} else
set.add(s.charAt(runner));
++runner;
}
maxLen = Math.max(maxLen, runner - walker);
return maxLen;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println(lengthOfLongestSubstring("ababc"));
}
}
解决思路:
用两个下标walker和runner组成一个不包含重复字符的窗口,runner每次向右移一个字符,若该字符与窗口中的各字符不重复,则继续右移。若重复,则walker移至与runner位置指向的字符相同的字符位置之后一位(比如说walker与runner组成的窗口中第三个字符与runner此时指向的字符相同,则将walker移动至窗口的第四个字符的位置),建立一个新窗口,继续循环
把这题的思路应用于本题,可求出字符串S中,以L[0].length为单位的各不重复子串,然后判断是否包含L。