有一类串串题通常需枚举所有的子串,再进行统计。
如果我们能O(1)得到一个子串,再用hash统计,1e4 的规模完全是可以过的
string和string_view的区别
string_view和string的区别:
string_view 是C++17所提供的用于处理只读字符串的轻量对象。这里后缀 view 的意思是只读的视图。
- 通过调用 string_view 构造器可将字符串转换为 string_view 对象。
- string 可隐式转换为 string_view。
- string_view 是只读的轻量对象,它对所指向的字符串没有所有权。
- string_view通常用于函数参数类型,可用来取代 const char* 和 const string&。
- string_view 代替 const string&,可以避免不必要的内存分配。
- string_view的成员函数即对外接口与 string 相类似,但只包含读取字符串内容的部分。
- string_view::substr()的返回值类型是string_view,不产生新的字符串,不会进行内存分配。
- string::substr()的返回值类型是string,产生新的字符串,会进行内存分配。
- string_view字面量的后缀是 sv。(string字面量的后缀是 s)
复杂度:
- substr
- string的复杂度是O(Linear in count)
- string_view的复杂度是O(constant)
- operator =
- string的复杂度是O(linear in size of str)
- string_view的复杂度是O(constant)
- operator ==
- string的复杂度是O(size)
- string_view的复杂度是O(size)
string to string_view:
string s = "hello"
string_view ss(s);
string_view to string:
inline std::string as_string(std::string_view v) {
return {v.data(), v.size()};
}
string(ss)
暴力切题
来 我们来秒困难题
Leetcode 1316. 不同的循环子字符串
题意:求所有aa格式的子串的个数
方法:枚举所有子串,string_view可以直接判断是否相等,再用unordered_map或者unordered_set统计
class Solution {
public:
int distinctEchoSubstrings(string text) {
unordered_set<string_view>st;
int n = text.size();
string_view s(text);
for(int i = 1;i <= n/2;i++) { // 枚举长度
for(int j = i;j+i <= n;j++) { // 枚举中心
// cout << s.substr(j-i, i) << " " << s.substr(j, i) << endl;
if(s.substr(j-i, i) == s.substr(j, i)) { // 直接比较,不需要逐一判断
st.insert(s.substr(j, i));
}
}
}
return st.size();
}
};
Leetcode 1044. 最长重复子串
题意:出现次数大于等于2次的子串称为重复子串,求最长的重复子串
方法:对于长度,有单调性,可以二分。假如指定长度,我们可以用string_view得到所有指定长度的子串,再通unordered_map统计
class Solution {
public:
inline std::string as_string(std::string_view v) {
return {v.data(), v.size()};
}
string check(string s, int len) {
string_view ss(s);
unordered_map<string_view, int>mp;
for(int i = 0;i <= ss.size()-len;i++) {
// cout << ss.substr(i, len) << endl;
mp[ss.substr(i, len)]++;
}
for(auto it = mp.begin(); it != mp.end();it++) {
if(it->second >= 2) return (it->first).data();
}
return "";
}
string longestDupSubstring(string s) {
int left = 1, right = s.size(), mid;
string ans;
while(left <= right) {
mid = (left + right) / 2;
string res = check(s, mid);
// cout << mid << " " << res << endl;
if(res != "") {
left = mid+1;
ans = res;
}
else right = mid-1;
}
return ans;
}
};
Leetcode187. 重复的DNA序列
题意:相当于上题的简化版,求指定长度的重复子串
方法:暴力统计
class Solution {
public:
vector<string> findRepeatedDnaSequences(string s) {
unordered_map<string_view, int>mp;
string_view ss(s);
for(int i = 0;i <= (int)s.size()-10;i++) {
mp[ss.substr(i, 10)]++;
}
vector<string>ans;
for(auto item : mp) {
if(item.second >= 2) ans.push_back(string(item.first));
}
return ans;
}
};
因为在加入哈希表的过程中,不会改变字符串的内容,因此使用string_view可以获得比substr更好的性能。(虽然对于本题这种较短的string会使用COW技术优化内存,故时间复杂度体现的不明显)。
参考:https://leetcode-cn.com/problems/repeated-dna-sequences/solution/c17-ha-xi-biao-jie-he-string_view-by-owe-z0oy/
Leetcode 面试题 17.13. 恢复空格
题意:给定一些单词,求给定字符串切割后,不在字典中的字母最小
方法:首先unordered_set统计字典,dp[i]表示前i个未识别的字符数,dp[i] = min(dp[i-1]+1, dp[j]),sbustr(j, i-j)可以用string_view
class Solution {
public:
int respace(vector<string>& dictionary, string sentence) {
unordered_set<string_view> s;
for (const string &str : dictionary)
s.insert(str);
int n = sentence.size();
vector<int> dp(n + 1);
string_view sv(sentence);
for (int i = 1; i <= n; ++i) {
dp[i] = dp[i - 1] + 1;
for (int j = 1; j <= i; ++j) {
string_view sub = sv.substr(j - 1, i - j + 1);
if (s.find(sub) != s.end())
dp[i] = min(dp[i], dp[j - 1]);
}
}
return dp[n];
}
};
Trie和AC自动机在其他题解中已经有了详细介绍,这里主要提供一个加速普通字符串哈希算法的方法。
原理就是利用C++ 17提供的string_view,减少子串查找过程中不必要的字符串创建开销。
与直接用string相比,运行时间从1600+ ms降低到不到400 ms。
参考:https://leetcode-cn.com/problems/re-space-lcci/solution/c-string_view-by-lucifer1004/
参考链接: