异位词(Anagram)是指由相同字母重新排列形成的不同单词或短语。在这个问题中,我们需要在字符串s中找出所有与字符串p构成异位词的子串,并返回这些子串的起始索引。
关键点解析:
示例验证:
以示例1为例:
最直观的解法是滑动窗口+排序比较:
cpp复制vector<int> findAnagrams(string s, string p) {
vector<int> res;
int wnd_size = p.size();
if(wnd_size > s.size()) return res;
sort(p.begin(), p.end());
for(int i = 0; i <= s.size() - wnd_size; i++) {
string temp = s.substr(i, wnd_size);
sort(temp.begin(), temp.end());
if(temp == p) {
res.push_back(i);
}
}
return res;
}
注意:这种方法在小规模数据上表现尚可,但当字符串较长时性能会显著下降,因为排序操作开销较大。
更高效的解法是使用哈希表记录字符出现次数:
cpp复制vector<int> findAnagrams(string s, string p) {
vector<int> res;
if(p.size() > s.size()) return res;
vector<int> pCount(26, 0);
vector<int> wndCount(26, 0);
// 初始化计数
for(int i = 0; i < p.size(); i++) {
pCount[p[i]-'a']++;
wndCount[s[i]-'a']++;
}
if(pCount == wndCount) res.push_back(0);
// 滑动窗口
for(int i = p.size(); i < s.size(); i++) {
// 移出窗口的字符
wndCount[s[i-p.size()]-'a']--;
// 进入窗口的字符
wndCount[s[i]-'a']++;
if(wndCount == pCount) {
res.push_back(i - p.size() + 1);
}
}
return res;
}
在实际编码中需要考虑以下特殊情况:
改进后的鲁棒性代码:
cpp复制vector<int> findAnagrams(string s, string p) {
vector<int> res;
if(p.empty() || s.empty() || p.size() > s.size())
return res;
// 统一转为小写(根据题目要求)
transform(p.begin(), p.end(), p.begin(), ::tolower);
transform(s.begin(), s.end(), s.begin(), ::tolower);
vector<int> pCount(26, 0);
vector<int> wndCount(26, 0);
for(int i = 0; i < p.size(); i++) {
if(!isalpha(p[i]) || !isalpha(s[i]))
return res; // 包含非字母字符
pCount[p[i]-'a']++;
wndCount[s[i]-'a']++;
}
if(pCount == wndCount) res.push_back(0);
for(int i = p.size(); i < s.size(); i++) {
if(!isalpha(s[i]) || !isalpha(s[i-p.size()]))
return res;
wndCount[s[i-p.size()]-'a']--;
wndCount[s[i]-'a']++;
if(wndCount == pCount) {
res.push_back(i - p.size() + 1);
}
}
return res;
}
为了验证两种解法的性能差异,我进行了以下测试:
测试环境:
测试用例:
小规模数据:
中等规模数据:
大规模数据:
结论:
哈希计数法在大规模数据下性能优势明显,适合实际应用场景。
可能原因:
解决方案:
测试用例设计建议:
进阶优化方向:
这种算法可以应用于:
可以修改哈希计数法,计算字符差异数而不是要求完全匹配。
需要使用更通用的哈希表而不是固定大小的数组。
这变成了一个更复杂的问题,可能需要使用前缀树等数据结构。
在实际编码中,我发现以下几点特别重要:
一个容易忽略的细节是窗口滑动时的计数更新顺序。我曾经因为先增加新字符计数再减少旧字符计数而导致错误,正确的顺序应该是先减后加。