LeetCode—438。文字列内のすべてのアナグラムを検索[文字列内のすべてのアナグラムを検索]-分析とコード[Java]
1.タイトル
文字列sと空でない文字列pが与えられた場合、s内のpの文字失読症のすべての部分文字列を見つけ、これらの部分文字列の開始インデックスを返します。
文字列には小文字の英字のみが含まれ、文字列sおよびpの長さは20100以下です。
説明:
- 脂肪族の単語は、同じ文字で配置が異なる文字列を指します。
- 回答が出力される順序は考慮されません。
例1:
输入:
s: "cbaebabacd" p: "abc"
输出:
[0, 6]
解释:
起始索引等于 0 的子串是 "cba", 它是 "abc" 的字母异位词。
起始索引等于 6 的子串是 "bac", 它是 "abc" 的字母异位词。
例2:
输入:
s: "abab" p: "ab"
输出:
[0, 1, 2]
解释:
起始索引等于 0 的子串是 "ab", 它是 "ab" 的字母异位词。
起始索引等于 1 的子串是 "ba", 它是 "ab" 的字母异位词。
起始索引等于 2 的子串是 "ab", 它是 "ab" 的字母异位词。
出典:LeetCode(LeetCode)
リンク:https://leetcode-cn.com/problems/find-all-anagrams-in-a-string
著作権はLeetCodeが所有しています。商用の再版については、公式の承認に連絡してください。非商用の再版については、出典を示してください。
2、分析とコード
1.スライディングウィンドウ
(1)考える
文字列pと同じ長さのスライディングウィンドウを設計し、文字列s内を左から右に移動して、ウィンドウ内の各位置の文字が文字列pと同じであるかどうかを判断します。
判断の効率を上げるために、数字と配列を使用して現在のウィンドウに同じ文字数を記録し、キュー記録ウィンドウの文字の種類を組み合わせてウィンドウをすばやく維持することができます。
文字列pに存在しない文字に遭遇した場合、ウィンドウの左側のインデックスをその位置の後ろにすばやく移動できます。
(2)コード
class Solution {
public List<Integer> findAnagrams(String s, String p) {
List<Integer> ans = new ArrayList<>();
if (s.length() == 0 || p.length() == 0)
return ans;
char[] cs = s.toCharArray();
char[] cp = p.toCharArray();
int wLen = p.length();//窗口长度
int len = 0, index = 0;//窗口中字符个数,窗口首字符位置
boolean[] hasChar = new boolean[26];//记录p中是否存在该字母
int[] numChar = new int[26];//记录当前窗口中还需出现各字母个数
for (int i = 0; i < 26; i++) {
//初始化
hasChar[i] = false;
numChar[i] = 0;
}
for (int i = 0; i < wLen; i++) {
//记录p中数据
hasChar[cp[i] - 'a'] = true;
numChar[cp[i] - 'a']++;
}
Queue<Character> win = new LinkedList<Character>();
for (int i = 0; i < s.length(); i++) {
if (hasChar[cs[i] - 'a'] == false) {
//出现p中不存在的字母,直接将窗口起点跳到下一字符处
while (len > 0) {
numChar[win.poll() - 'a']++;
len--;
}
index = i + 1;
continue;
}
if (numChar[cs[i] - 'a'] > 0) {
//出现p中存在且当前窗口中个数不足的字母
win.offer(cs[i]);
numChar[cs[i] - 'a']--;
len++;
if (numChar[cs[i] - 'a'] == 0 && len == wLen) {
//此时窗口范围为p的字母异位词的子串
ans.add(index);
numChar[win.poll() - 'a']++;
len--;
index++;
}
continue;
}
while (len > 0 && win.peek() != cs[i]) {
//出现p中存在但当前窗口中个数已达到需求的字母
numChar[win.poll() - 'a']++;
len--;
index++;
}
win.poll();
index++;
win.offer(cs[i]);
}
return ans;
}
}
(3)結果
実行時間:10ミリ秒、
すべてのJava送信でユーザーの69.58%を上回っています。メモリ消費量:40.2 MB、すべてのJava送信でユーザーの30.75%を上回っています。
2.スライディングウィンドウの最適化
(1)考える
上記の方法では、キュー内の要素は配列内の位置に従って整然と配置され、添え字を介して直接アクセスできます。この場合、キューエンティティを作成する必要はなく、ウィンドウも作成されます。範囲は、キューのメンテナンスオーバーヘッドを削減するために、開始点と長さを介して配列に直接マークされます。
(2)コード
class Solution {
public List<Integer> findAnagrams(String s, String p) {
List<Integer> ans = new ArrayList<>();
if (s.length() == 0 || p.length() == 0)
return ans;
char[] cs = s.toCharArray();
char[] cp = p.toCharArray();
int wLen = p.length();//窗口长度
int len = 0, index = 0;//窗口中字符个数,窗口首字符位置
boolean[] hasChar = new boolean[26];//记录p中是否存在该字母
int[] numChar = new int[26];//记录当前窗口中还需出现各字母个数
for (int i = 0; i < 26; i++) {
//初始化
hasChar[i] = false;
numChar[i] = 0;
}
for (int i = 0; i < wLen; i++) {
//记录p中数据
hasChar[cp[i] - 'a'] = true;
numChar[cp[i] - 'a']++;
}
for (int i = 0; i < s.length(); i++) {
if (hasChar[cs[i] - 'a'] == false) {
//出现p中不存在的字母,直接将窗口起点跳到下一字符处
while (len > 0)
numChar[cs[index + --len] - 'a']++;
index = i + 1;
continue;
}
if (numChar[cs[i] - 'a'] > 0) {
//出现p中存在且当前窗口中个数不足的字母
numChar[cs[i] - 'a']--;
len++;
if (numChar[cs[i] - 'a'] == 0 && len == wLen) {
//此时窗口范围为p的字母异位词的子串
ans.add(index);
numChar[cs[index++] - 'a']++;
len--;
}
continue;
}
while (len > 0 && cs[index] != cs[i]) {
//出现p中存在但当前窗口中个数已达到需求的字母
numChar[cs[index++] - 'a']++;
len--;
}
index++;
}
return ans;
}
}
(3)結果
実行時間:5ミリ秒、
すべてのJava送信でユーザーの93.68%を上回っています。メモリ消費量:39.7 MB、すべてのJava送信でユーザーの84.86%を上回っています。
3、その他
何もありません。