[1392].最长快乐前缀

Posted Debroon

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[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 12310+4=1234
  • 右边: 新 字 母 ∗ 1 0 l e n − 1 + 当 前 字 母 新字母 * 10^{len-1} + 当前字母 10len1+,如 4 ∗ 1 0 3 + 123 = 4123 4*10^{3}+123=4123 4103+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].最长快乐前缀的主要内容,如果未能解决你的问题,请参考以下文章

模式匹配:滚动哈希到 Rabin-Karp 算法

模式匹配:滚动哈希到 Rabin-Karp 算法

JavaScript笔试题(js高级代码片段)

leetcode-14.最长公共前缀(图)

51nod 1392 装盒子(费用流)

LC 最长公共前缀