题解 | #长度为 K 的重复字符子串#
长度为 K 的重复字符子串
http://www.nowcoder.com/practice/eced9a8a4b6c42b79c95ae5625e1d5fd
题意理解
以长度为k的子串中如果某个字符出现多次,则判定该子串符合条件。计算一共有多少这样的子串。
方法一
枚举
从左向右遍历字符串,取出其中所有长度为k的子串。对于每个子串也遍历一遍,使用一个map<char, bool>类型find记录某个字符是否出现过,如果遇到了某个字符对应的find为真,表明该字符在子串中重复出现,此时答案ans加1并且跳出该子串的循环。
具体代码如下:
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param s string字符串
* @param k int整型
* @return int整型
*/
int numKLenSubstrRepeats(string s, int k) {
// write code here
int ans = 0;
for (int i=0;i<s.size()-k+1;i++)
{
string ss = s.substr(i,k);
map<char, bool> find;
for (int j=0;j<ss.size();j++)
{
//找到重复字符要及时退出
if (find.count(ss[j])) {ans++; break;}
else find[ss[j]] = 1;
}
find.clear();
}
return ans;
}
};
时间复杂度: 。双重遍历,外层遍历长度为N的字符串中每个字符作为子串的起点,内层遍历每个长度为k的子串。
空间复杂度: 。因为使用了clear()函数,map类型变量的大小最大为子串的长度k。
方法二
滑动窗口
因为两个相邻的子串,只有头尾不同,中间相同,所以尝试用滑动窗口代替内部对子串的循环。用map变量find记录某字符是否出现过,用map变量index记录某个字符在原字符串中的位置。用count记录当前所有字符都不重复时的字符个数;当时,说明一个子串中无重复字符,此时整个窗口右移一位。用begin记录这一段不重复字符的起始位置。
从左向右遍历原字符串:
1、遇到未出现的字符就令对应的find为真。
2、遇到出现过的字符#,以从begin到重复字符#第一次出现的位置的这个区间(begin~index[#])中的字符为开头的子串都包含重复字符,ans一次性加上这段区间的长度,窗口缩短并右移一段距离,更新count、begin、index、find。
注意字符串的结尾要特殊处理。以begin~index[#]这段区间中的字符为开头时,后面不一定够取k个字符组成子串。
以"abcbefe",4为例,过程示意图如下:
具体代码如下:
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param s string字符串
* @param k int整型
* @return int整型
*/
int numKLenSubstrRepeats(string s, int k) {
// write code here
int ans = 0, count = 0, begin = 0;
map<char, bool> find;
map<char, int> index;
for (int i=0;i<s.size();i++)
{
//count用来记录当前子串中有多少字符
count++;
//子串长度超过k后,第一个字符就要移除
if (count>k)
{
find[s[begin]] = false;
begin++;
count--;
}
//如果子串中未出现该字符,就更新find
if (!find[s[i]])
{
find[s[i]] = true;
index[s[i]] = i;
}
else
{
//出现重复字符后,若干子串都包含重复字符。
int t = s.size() - begin - k;
ans = ans + min(index[s[i]] - begin, t) + 1;
count = count - (index[s[i]] - begin) - 1;
for (int j=begin;j<index[s[i]];j++)
find[s[j]] = false;
begin = index[s[i]] + 1;
index[s[i]] = i;
//字符串末尾要特殊处理
if (begin > (s.size()-k)) break;
}
}
return ans;
}
};
时间复杂度: 。虽然有双重遍历,但是内部遍历加起来不超过原字符串长度;即每个字符只在外部循环遍历一次,内部循环遍历一次。
空间复杂度: 。两个map类型变量的大小最大为字符的个数k。