字符串hash暴力串串题

我们通过类似进制的方法对字符串hash,可以得到类似前缀和的性质,可以快速得到子串的hash值
而hash值和字符串是一一对应的(不考虑哈希冲突),因此可以比较和统计

Leetcode 28. 实现 strStr()

题意:字符串查找
方法:用字符串哈希代替KMP

class Solution {
public:
    typedef long long ll;
    typedef unsigned long long ull;
    const ull base = 233;
    vector<ull>p, h;

    void init(string s)
    {
        ll n = s.size();
        p.resize(n+1, 1);
        h.resize(n+1, 0);

        for(int i=1;i<=n;i++)  h[i] = h[i-1]*base + (ull)s[i-1];
        for(int i=1;i<=n;i++)  p[i] = p[i-1]*base;
    }

    inline ull gethash(int l,int r)  // 计算第l个 到第r的hash值
    {
        return h[r] - h[l-1]*p[r-l+1];
    }

    int strStr(string haystack, string needle) {
        if(needle == "")  return 0;
        ull target = 0;
        for(char ch : needle) target = target*base + (ull)ch;

        init(haystack);
        int n = haystack.size(), m = needle.size();
        int i = 1, j = m;
        while(j <= n) {
            // cout << gethash(i, i+m-1) << " " << target << endl;
            if(gethash(i, j) == target)  return i-1;
            i++;j++;
        }
        return -1;
    }
};

Leetcode 49. 字母异位词分组

题意:将属于相同排序的字符串放到一起
方法:将单词sort之后在hash,hash值相同的放一起(其实sort之后没必要hash了...)

class Solution {
public:
    int hash(string s)
    {
        unsigned int res = 0;
        unsigned int base = 233;
        sort(s.begin(), s.end());
        for(char ch : s)
            res = res*base + ch;  // 自然溢出
        return (int)res;
    }

    vector<vector<string>> groupAnagrams(vector<string>& strs) {
        unordered_map<int, vector<string>>mp;
        for(string s : strs)
            mp[hash(s)].push_back(s);
        vector<vector<string>>ans;
        for(auto it = mp.begin(); it != mp.end();it++)
            ans.push_back(it->second);
        
        return ans;
    }
};

Leetcode 336.回文对

题目:求不同的两个单词拼成的回文对
方法:如果正反哈希值相同,则认为是回文的。预处理所有单词的正反哈希值,可以O(1)得到拼接后的正反哈希值

class Solution {
public:
    typedef long long ll;
    typedef unsigned long long ull;
    const ull base = 233;

    ull getHash(string& s, int n) {
        ull hash = 0;
        // int n = s.size();
        for(int i = 0;i < n;i++)  hash = hash*base + s[i]-'a';
        return hash;
    }
    ull getRHash(string& s, int n) {
        ull hash = 0;
        // int n = s.size();
        for(int i = n-1;i >= 0;i--)  hash = hash*base + s[i]-'a';
        return hash;
    }

    vector<vector<int>> palindromePairs(vector<string>& words) {
        int n = words.size();
        // hs.resize(n);
        // rhs.resize(n);
        // mul.resize(305);  // resize会超时
        ull hs[n], rhs[n], len[n];
        ull mul[305];
        for(int i  = 0;i < n;i++) {
            len[i] = words[i].size();   // 避免每次去求size,也是一个常数级优化 1036ms->568ms
            hs[i] = getHash(words[i], len[i]);
            rhs[i] = getRHash(words[i], len[i]);
        }
        for(int i = 0;i < 305;i++)  mul[i] = (i==0?1:mul[i-1]* base);

        vector<vector<int>>ans;
        for(int i = 0;i < n;i++) {
            for(int j = 0;j < n;j++) {
                if(i == j)  continue;
                ull left = hs[i]*mul[len[j]] + hs[j];
                ull right = rhs[i] + rhs[j]*mul[len[i]];
                if(left == right)  ans.push_back({i,j});
            }
        }
        return ans;
    }
};

Leetcode 214. 最短回文串

题目:在字符串前面添加最少的字母,使其变成一个回文串
方法:找到最长的回文前缀,要补的就是最少的。枚举每个前缀,用hash判断是否回文。

class Solution {
public:
    typedef unsigned long long ull;
    ull base = 233;
    string shortestPalindrome(string s) {
        if(s=="") return "";
        int n = s.size(), pos;
        ull hs = 0, rhs = 0, mul = 1;
        for(int i = 0;i < n;i++) {
            hs = hs*base + s[i]-'a';
            rhs = rhs + (s[i]-'a')*mul;
            mul *= base;
            if(hs == rhs)  pos = i;
        }
        string tmp = s.substr(pos+1);
        reverse(tmp.begin(), tmp.end());
        return tmp+s;
    }
};
posted @ 2021-12-25 23:40  Rogn  阅读(22)  评论(0编辑  收藏  举报