字符串hash暴力串串题
我们通过类似进制的方法对字符串hash,可以得到类似前缀和的性质,可以快速得到子串的hash值
而hash值和字符串是一一对应的(不考虑哈希冲突),因此可以比较和统计
Leetcode 28. 实现 strStr()
题意:字符串查找
方法:用字符串哈希代替KMP
class Solution {
public:
typedef long long ll;
typedef unsigned long long ull;
const ull base = 233;
vectorp, 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> groupAnagrams(vector& strs) {
unordered_map>mp;
for(string s : strs)
mp[hash(s)].push_back(s);
vector>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> palindromePairs(vector& 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>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;
}
};