CFGYM103176C-camelCaseCounting (SAM/SA)

CFGYM103176C-camelCaseCounting

Mean

给定一个长为\(n\)的字符串,存在大小写字母,问本质不同的小写字母开头的串的数目。\(n<=1e6\)。

Sol

由于题目只给了\(1S\),可以大胆猜测是个\(O(n)\)相关的题。

考虑建出\(SAM\),每个状态中维护最大的\(endpos\)位置信息\(f[u]\).

那么每个状态对答案的贡献取决于\([f[i]-node[i].len,f[i]-node[node[i].fa].len]\)中有多少个小写字母。

前缀和预处理一下,最后直接计数即可。

复杂度\(O(n)\).

比较奇怪的是\(SAM\)跑得没\(SA\)慢好多,先写\(SAM\)的做法吧,回头更\(SA\)的做法。

Code(SAM)

#include <iostream>
#include <cstring>
#include <map>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 1e6+10;
int tot = 1, last = 1;

struct Node
{
    int len, fa;
    int ch[53];
}node[N*2];
char str[N];
ll f[N*2], ans;
int h[N*2], e[N*3], ne[N*3], idx;

int getch(char x){
    if(x>='a'&&x<='z')return x-'a';
    else return 26+x-'A';
}
void extend(int c,int id)
{
    int p = last, np = last = ++ tot;
    f[tot] = id;
    node[np].len = node[p].len + 1;
    for (; p && !node[p].ch[c]; p = node[p].fa) node[p].ch[c] = np;
    if (!p) node[np].fa = 1;
    else
    {
        int q = node[p].ch[c];
        if (node[q].len == node[p].len + 1) node[np].fa = q;
        else
        {
            int nq = ++ tot;
            node[nq] = node[q], node[nq].len = node[p].len + 1;
            node[q].fa = node[np].fa = nq;
            for (; p && node[p].ch[c] == q; p = node[p].fa) node[p].ch[c] = nq;
        }
    }
}

void add(int a, int b)
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}

void dfs(int u)
{
    for (int i = h[u]; ~i; i = ne[i])
    {
        dfs(e[i]);
        f[u] =max(f[u], f[e[i]]);
    }
}

int sum[N];

int main()
{
    scanf("%s", str+1);
    int lens=strlen(str+1);
    for (int i = 1; str[i]; i ++ ) extend(getch(str[i]),i);
    memset(h,-1,sizeof(h));
    for(int i=2;i<=tot;++i){
        add(node[i].fa,i);
    }
    dfs(1);
    for(int i = 1;i<=lens;++i){
        if(str[i]>='a'&&str[i]<='z')sum[i]=sum[i-1]+1;
        else sum[i]=sum[i-1];
    }

    for(int i=2;i<=tot;++i){
        int l = f[i]-node[i].len+1;
        int r = f[i]-node[node[i].fa].len;
        ans = ans+sum[r]-sum[l-1];
    }
    printf("%lld\n",ans);
    return 0;
}

Code(SA)

wating.

上一篇:[leetcode 2021-1-8] 二、最长重复子串


下一篇:21. 合并两个有序链表