string_view暴力串串题

有一类串串题通常需枚举所有的子串,再进行统计。
如果我们能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)

std::string_view 的局限:

  • 追加和拼接会触发拷贝
  • 创建 string_view 的 string 不能修改,否则再次访问对应的 string_view 时,行为未定义

复杂度:

  • 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/

参考链接:

  1. How to correctly create std::string from a std::string_view?
  2. C++17特性 string_view substr只要常数复杂度,且省内存
posted @ 2021-12-25 21:54  Rogn  阅读(206)  评论(0编辑  收藏  举报