[1392].最长快乐前缀
Posted Debroon
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[1392].最长快乐前缀相关的知识,希望对你有一定的参考价值。
[1392].最长快乐前缀
题目
题目链接:https://leetcode-cn.com/problems/longest-happy-prefix/
快乐前缀:既是前缀,也是后缀的字符串。
如 level
,快乐前缀是 l
。
如 ababab
,最长快乐前缀是:abab
。
函数原型
string longestPrefix(string s) {}
枚举
遍历一次 s 的所有前缀,同时看看前缀是否也是后缀,由于找最长前缀,所以从后往前遍历。
前缀范围:s[0···len-1]
后缀范围:s[s.length-len···s.length-1]
string longestPrefix(string s) {
for(int len = s.length()-1; len >= 1; len --) // 最长前缀非字符串本身,需要 -1
if( equal(s, 0, len-1, s.length()-len, s.length()-1) )
return s.substr(0, len);
return "";
}
bool equals(string s, int l1, int r1, int l2, int r2) {
for(int i=l1, j=l2; i<=r1 && j<=r2; i++, j++) // s[l1, r1] == s[l2, r2]?
if(s[i] != s[j]) // s[i] == s[j]?
return false;
return true;
}
超时。
Rabin-Karp(哈希优化)
我们需要比较某一个前缀是否是后缀,完全可以比较俩个子串的哈希值,哈希只需要 O ( 1 ) O(1) O(1)。
如果不相等,前缀-1、后缀-1,看更短的子串。
比如 1234 -> 123,(1234-4)/10=123。
但因为计算机整型限制,采取的是:(1234 % M) / 10。
但是我们是想:(1234 / 10) % M。
- (1234 % M) / 10 != (1234 / 10) % M
除法 调换顺序,结果就不一样了。
那么,从长的哈希值倒推到短的哈希值这个思路就不可行。
我们应该从短的哈希值开始计算,叠加到长的哈希值,把每次中间结果存起来。
- 左边: 当 前 字 母 ∗ 10 + 新 字 母 当前字母 * 10 + 新字母 当前字母∗10+新字母,如 123 ∗ 10 + 4 = 1234 123 * 10 + 4 = 1234 123∗10+4=1234
- 右边: 新 字 母 ∗ 1 0 l e n − 1 + 当 前 字 母 新字母 * 10^{len-1} + 当前字母 新字母∗10len−1+当前字母,如 4 ∗ 1 0 3 + 123 = 4123 4*10^{3}+123=4123 4∗103+123=4123
class Solution {
long MOD = (long)(1e9 + 7); // 素数,防止溢出
long pow26[];
public:
string longestPrefix(string s) {
long *pow26 = new long[s.length()]; // 提前计算26的幂
pow26[0] = 1;
for(int i=1; i<s.length(); i++)
pow26[i] = pow26[i - 1] *26 % MOD; // 防止溢出
long *left_hash = new long[s.length()];
left_hash[0] = s[0] - 'a';
for(int i=1; i<s.length(); i++)
left_hash[i] = (left_hash[i-1] * 26 + s[i] - 'a') % MOD; // 26 进制
long *right_hash = new long[s.length()];
right_hash[s.length()-1] = s[s.length()-1] - 'a';
for(int i=s.length() - 2; i >= 0; i--)
right_hash[i] = ((s[i] - 'a') * pow26[s.length() - i - 1]+right_hash[i+1])%MOD;
for(int len = s.length()-1; len >= 1; len --) // 最长前缀非字符串本身,需要 -1
if( left_hash[len-1] == right_hash[s.length()-len] && equals(s, 0, len-1, s.length()-len, s.length()-1) )
return s.substr(0, len);
return "";
}
bool equals(string s, int l1, int r1, int l2, int r2) {
for(int i=l1, j=l2; i<=r1 && j<=r2; i++, j++) // s[l1, r1] == s[l2, r2]?
if(s[i] != s[j]) // s[i] == s[j]?
return false;
return true;
}
};
其实这种将字符串映射成整数的编码方式思想叫 Rabin-Karp。
class Solution {
public:
string longestPrefix(string s) {
int n = s.size();
int prefix = 0, suffix = 0;
int base = 31, mod = 1000000007, mul = 1;
int happy = 0;
for (int i = 1; i < n; ++i) {
prefix = ((long long)prefix * base + (s[i - 1] - 97)) % mod;
suffix = (suffix + (long long)(s[n - i] - 97) * mul) % mod;
if (prefix == suffix) {
happy = i;
}
mul = (long long)mul * base % mod;
}
return s.substr(0, happy);
}
};
KMP
class Solution {
public:
string longestPrefix(string s) {
int n = s.size();
vector<int> fail(n, -1);
for (int i = 1; i < n; ++i) {
int j = fail[i - 1];
while (j != -1 && s[j + 1] != s[i]) {
j = fail[j];
}
if (s[j + 1] == s[i]) {
fail[i] = j + 1;
}
}
return s.substr(0, fail[n - 1] + 1);
}
};
以上是关于[1392].最长快乐前缀的主要内容,如果未能解决你的问题,请参考以下文章