牛客网暑期ACM多校训练营(第一场) I Substring 求字符串子串数+脑洞

时间限制:C/C++ 2秒,其他语言4秒
空间限制:C/C++ 524288K,其他语言1048576K
64bit IO Format: %lld

题目描述

Two strings u 1 u 2 … u k and v 1 v 2 … v l are isomorphic if and only if k = l and there exists a injection g such that u i = g(v i) for all i ∈ {1, 2, …, k}.
Note that a function f is injection if and only if f(x) ≠ f(y) for all x ≠ y.
Bobo would like to choose some strings from all n(n +1)/2 substrings of the given string s 1 s 2 … s n.
Find out the maximum number of strings he may choose so that no two chosen strings are isomorphic.

输入描述:

The input consists of several test cases and is terminated by end-of-file.
The first line of each test case contains an integer n.
The second line contains a string s1, s2, ..., sn.

输出描述:

For each test case, print an integer which denotes the result.


输入例子:
4
abaa
4
abab


输出例子:
6
4

题意 定义两个s1,s2串相似 当且仅当存在一种字母一一对应的映射F 使得s1=F(s2)
现给出一个字符串,有你求一个集合的最大 大小,该集合中的所有子串均不相似。
字符串只包含abc三种字母 长度为5e5

解题思路:
这一题对我来说真的就是神仙题,完全想不到。。。。。
所以根据题解思路走,
题解说的实在太简要,想了好久也只能想明白个大概。。 如果说的不对,那是正常操作。
首先,对于数据范围 字符串最多包含三个字母。
所以考虑枚举映射方案(6种)。
首先考虑合法的情况
将子串按照起始位置和终止位置分类
一种子串符合题目要求的条件是 当且仅当该种子串在6种映射方案中均不相同。
这时候,就会有原串的一种子串6种不同的串
再考虑不合法的情况
如果一种子串存在一种映射 使得它和另一种子串相似。
那么 对于这两种子串,对于每一种映射,都有唯一的一种映射与之相似,那么 这些子串在六种映射中所对应的不同的串合起来也只有六种。( 说不太明白,自己找个例子 推一下就很清楚了)

但这样考虑还是有一个bug,当一个子串全是一种字母的时候,它所映射的不同子串只有3种。
所以 我们枚举玩所有的映射方案后,把他们所有的子串都找出来 就能推出集合最大的大小了。
求子串数目是后缀自动机可以利用后缀自动机在O(n)的时间内解决。
最后答案就是 (不同子串数量 + 3 × 单一字符的串) / 6

只能讲成这样了,这题对我来说难度太高了。。。。。
另外由于多组输入,所以初始化的时候要注意,一不小心就T了。。。。。。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#define N 2000050

using namespace std;
typedef long long LL;

char s[N];
int ch[N][5],len[N],link[N],rd[N],last,tot,rt,n;
LL F[N],ans[N];

inline void ut(LL &x,LL y) { x = max(x,y); }
int maps[3]={1,2,3};
void add(int pos) {
    int x = maps[s[pos] - 'a'] , p = last , np = ++tot;
    last = np;
    len[np] = len[p] + 1;
    F[np] = 1LL;

    while (p && !ch[p][x]) ch[p][x] = np , p = link[p];
    if (!p)
        link[np] = rt;
    else {
        int q = ch[p][x];
        if (len[q] == len[p] + 1)
            link[np] = q;
        else {
            int nq = ++tot;
            len[nq] = len[p] + 1;
            memcpy(ch[nq],ch[q],sizeof(ch[q]));
            link[nq] = link[q];
            link[q] = link[np] = nq;
            while (p && ch[p][x] == q) ch[p][x] = nq , p = link[p];
        }
    }
    return ;
}
void getsize(){
    for (int i=1;i<=tot;i++) rd[ link[i] ]++;
    queue<int> q;
    for (int i=1;i<=tot;i++) {
        if (!rd[i]) q.push(i);
        ans[i]=0;
    }
    while (!q.empty()) {
        int u = q.front(); q.pop();
        F[ link[u] ] += F[u];
        if (--rd[ link[u] ] == 0) q.push(link[u]);
        ut( ans[ len[u] ] , F[u]);
        ans[len[u]]=max(ans[len[u]],F[u]);
    }
}
void init(){
    rt = last =tot=1;
    memset(ch,0,sizeof ch);
    //memset(len,0,sizeof len);
    memset(rd,0,sizeof rd);
    //memset(F,0,sizeof F);
    //memset(link,0,sizeof link);
}
int main() {
    while(~scanf("%d",&n)){
        scanf("%s",s+1);
        init();
        do{
            //printf("%s",s+1);
            last=1;
            for (int i=1;i<=n;i++) add(i);
        }while(next_permutation(maps,maps+3));
        getsize();
        long long ans=0;
        for(int i=1;i<=tot;i++){
            ans+=(len[i]-len[link[i]]);
        }
        int longes=1;
        int maxs=1;
        for(int i=1;i<=n;i++){
            while(i<=n && s[i]==s[i+1]){
                i++;
                longes++;
            }
            maxs=max(longes,maxs);
            longes=1;
        }
        cout<<(ans+3*maxs)/6<<endl;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/lifelikes/article/details/81132511
今日推荐