题解 - 神秘字符串(mystery)

题目描述

在一个古老的村庄里, 住着一位名叫小唐的年轻女孩。 她对回文情有
独钟, 喜欢挑战字符串中回文子串的权重。 她手中拿着一串由小写字母组
成的神秘字符串, 她想知道在这个字符串中, 回文子串的最大权重是多少。

在她的冒险中, 她遇到了一位名叫艾德加的老者, 他对字符串中的回
文有着独特的见解。 小唐向他请教, 希望从他那里获得一些指导。

在村庄的广场上, 小唐和艾德加一起坐下, 开始分析这个神秘字符串。
他们发现, 字符串中的每个回文子串都有一个“权重”, 这个值等于该回文
子串在字符串中出现的次数乘以其长度。 小唐着迷于这个发现, 她决心找
出字符串中回文子串的最大权重。

小唐和艾德加一起思考, 尝试各种方法来解决这个问题。 他们在村庄
里的每个角落寻找灵感, 希望能够找到答案。 他们决心不放弃, 直到找到
问题的解决方案。

输入

输入为一个字符串 S, 字符串中只包含小写字母。

输出

输出为一个整数, 表示给定字符串 S 中回文子串的最大权重。

样例

【样例 1 输入】
abacaba
【样例 1 输出】
7
【样例 2 输入】
www
【样例 2 输出】
4
【样例 1 说明】
字符串 S 的子串 S[i, j] 是指任意非空字符串 si, si+1, …sj 的集合, 其中
1 ≤ i ≤ j ≤ |S|。 任何字符串也都是其自身的子串。
回文子串是指从左向右和从右向左读取相同的序列。 例如, 字符串
“level” 中的回文子串包括 l, e, v, eve 等。
在第一个样例中有 7 个回文子串 a、 b、 c、 aba、 aca、 bacab、
abacaba。
 a 在给定的字符串中出现了 4 次, 其权重为 4×1=4。
 b 在给定的字符串中出现了 2 次, 其权重为 2×1=2。
 c 在给定的字符串中出现了 1 次, 其权重为 1×1=1。
 aba 在给定的字符串中出现了 2 次, 其权重为 2×3=6。
 aca 在给定的字符串中出现了 1 次, 其权重为 1×3=3。
 bacab 在给定的字符串中出现了 1 次, 其权重为 1×5=5。
 abacaba 在给定的字符串中出现了 1 次, 其权重为 1×7=7。
因此, 回文子串的最大权重为 7。
【数据范围】
|S| 表示字符串的长度。
对于 40% 的数据 1 ≤ |S| ≤ 100;
对于 80% 的数据 1 ≤ |S| ≤ 1000 ;
对于 100% 的数据 1 ≤ |S| ≤ 5000;

思路分析

由于s的长度最多为5000,所以我们直接暴力枚举所有的子串,

若当前字串为回文串,则用map记录数量。

最终,遍历map,取最大值即可。

注意,在枚举并判断回文串时,不可以用下列代码:

for(int len = 1;len <= s.size();len++)
    for(int i = 0;i + len - 1 < s.size();i++){
        string tmp = s.substr(i,len);
        string tmpp = tmp;
        reverse(tmpp.begin(),tmpp.end());
        if(tmp == tmpp) mp[tmp]++;
    }

上述代码第一维枚举长度,第二位枚举起点,看似是O(n^2)的时间复杂度,但实际并不是!

因为substr函数的复杂度是O(n)的,故上述代码的复杂度实际为O(n^3),会超时

因此,我们可以换一种枚举方式,并通过字符串哈希来判断是否为回文串。

for(int i = 1;i <= n;i++){
    string tmp;
    for(int j = i;j <= n;j++){
        tmp += s[j];
        if(get1(i,j) == get2(n - j + 1,n - i + 1)) mp[tmp]++;
    }
}

对于两个string字符串a和b,a += b的时间复杂度为O(len(b)),而a = a + b的时间复杂度为O(len(a) + len(b)),故上述代码枚举的复杂度也为O(n^2),

而我们通过预处理出正序字符串和逆序字符串的哈希表,通过判断get1(i,j)get2(n - j + 1,n - i + 1)是否相等来判断字符串是否为回文串,时间复杂度可以降为O(1)。

ULL get1(int l, int r){
    return h1[r] - h1[l - 1] * p[r - l + 1];
}

ULL get2(int l, int r){
    return h2[r] - h2[l - 1] * p[r - l + 1];
}
// 预处理部分
h1[0] = h2[0] = 0;
p[0] = 1;

for(int i = 1;i <= n;i++){
    h1[i] = h1[i - 1] * P + s[i];
    h2[i] = h2[i - 1] * P + s[n - i + 1];
    p[i] = p[i - 1] * P;
}

时间复杂度为O(n^2)。

此外,若此题数据进一步加强,可以用高级算法-回文树来解决,复杂度为O(nlogn)。

代码1(暴力枚举)

#include<bits/stdc++.h>

using namespace std;

typedef long long LL;
typedef unsigned long long ULL;

const int N = 5000 + 10,P = 131;

char s[N];
map<string,int> mp;
LL res;
ULL h1[N],h2[N],p[N];

ULL get1(int l, int r){
    return h1[r] - h1[l - 1] * p[r - l + 1];
}

ULL get2(int l, int r){
    return h2[r] - h2[l - 1] * p[r - l + 1];
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);

    cin >> s + 1;

    int n = strlen(s + 1);

    h1[0] = h2[0] = 0;
    p[0] = 1;

    for(int i = 1;i <= n;i++){
        h1[i] = h1[i - 1] * P + s[i];
        h2[i] = h2[i - 1] * P + s[n - i + 1];
        p[i] = p[i - 1] * P;
    }

    for(int i = 1;i <= n;i++){
        string tmp;
        for(int j = i;j <= n;j++){
            tmp += s[j];
            if(get1(i,j) == get2(n - j + 1,n - i + 1)) mp[tmp]++;
        }
    }
    
    for(auto it = mp.begin();it != mp.end();it++){
        LL len = it->first.size();
        res = max(res,len * it->second);
    }

    cout << res;

    return 0;
}

代码2(回文树)

#include<bits/stdc++.h>

using namespace std;

typedef long long LL;

const int N = 5000 + 10;

char s[N];

int tot,last;
struct Tree{
	int son[100];
	int len;
	int fail;
    int cnt;
}tr[N];

void init(){
	tr[1].len = -1,tr[0].len = 0;
	tr[1].fail = 0,tr[0].fail = 1;
	tot = 1,last = 1; 
}

int getfail(int x,int i){
	while(s[i - tr[x].len - 1] != s[i]) x = tr[x].fail;
	
	return x;
}

void insert(int x,int i){
	int fa = getfail(last,i);
	int j = tr[fa].son[x]; 
	if(!j){ 
		j = ++tot;
		tr[fa].son[x] = j;
		tr[j].len = tr[fa].len + 2; 
		int tmp = getfail(tr[fa].fail,i);
		if(fa != 1) tr[j].fail = tr[tmp].son[x];
	}
    tr[j].cnt++;
	last = j;
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);

    cin >> s + 1;
    
    int n = strlen(s + 1);

    init();

    for(int i = 1;i <= n;i++) insert(s[i] - 'A',i);

    for(int i = tot;i >= 2;i--){
        tr[tr[i].fail].cnt += tr[i].cnt;
    }

    LL res = 0;
    for(int i = 2;i <= tot;i++) res = max(res,(LL)tr[i].len * tr[i].cnt);

    cout << res;

    return 0;
}

相关推荐

  1. 题解 - 神秘字符串mystery

    2024-07-19 20:36:02       21 阅读
  2. 修改字符串(c++题解)

    2024-07-19 20:36:02       54 阅读
  3. 力扣题解(交错字符串

    2024-07-19 20:36:02       25 阅读
  4. 力扣题解(环绕字符串中唯一的子字符串

    2024-07-19 20:36:02       16 阅读
  5. leetcode 字符串相关题目

    2024-07-19 20:36:02       48 阅读
  6. 题目 2850: 输出亲朋字符串

    2024-07-19 20:36:02       36 阅读
  7. python/c++ Leetcode题解——2744. 最大字符串配对数目

    2024-07-19 20:36:02       47 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-19 20:36:02       66 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-19 20:36:02       70 阅读
  3. 在Django里面运行非项目文件

    2024-07-19 20:36:02       57 阅读
  4. Python语言-面向对象

    2024-07-19 20:36:02       68 阅读

热门阅读

  1. ARP安全简介

    2024-07-19 20:36:02       21 阅读
  2. 瑞芯微RGA HAL层报错集锦

    2024-07-19 20:36:02       21 阅读
  3. 离散型随机变量为何不是左连续?

    2024-07-19 20:36:02       24 阅读
  4. vue图片存放在public目录和src/assets目录下的区别

    2024-07-19 20:36:02       20 阅读
  5. 根目录满迁移docker文件

    2024-07-19 20:36:02       19 阅读
  6. docker pull 拉取失败更换源

    2024-07-19 20:36:02       21 阅读
  7. Dubbo 的泛化调用

    2024-07-19 20:36:02       20 阅读
  8. WebKit 引擎:CSS 悬停效果的魔法师

    2024-07-19 20:36:02       18 阅读
  9. selenium.common.exceptions.NoAlertPresentException: Message:

    2024-07-19 20:36:02       18 阅读
  10. 聚类数优化:探索Sklearn中的策略与实践

    2024-07-19 20:36:02       21 阅读