[HAOI 2016] 找相同字符

Posted evenbao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[HAOI 2016] 找相同字符相关的知识,希望对你有一定的参考价值。

[题目链接]

         https://www.lydsy.com/JudgeOnline/problem.php?id=4566

[算法]

         首先 , 子串是后缀的前缀

         考虑拼接两个字符串 , 中间用不可见字符隔开 , 求出该字符串的后缀数组

         那么前缀相同的后缀一定排名一定接近

         而我们又知道lcp(i , j) = min{ height[rk[i] + 1] , height[rk[i] + 2] ... , height[rk[j]] }

         维护一个单调递增的后缀数组即可

        时间复杂度 : O(NlogN)(N = |A| + |B|)

[代码]

         

#include<bits/stdc++.h>
using namespace std;
#define N 400010
const int ALPHA = 26;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;

struct info
{
    int height;
    int cnta , cntb;
} ;

int n , m , len;
int height[N] , rk[N] , sa[N];
char s[N] , s1[N] , s2[N];

template <typename T> inline void chkmin(T &x , T y) { x = min(x , y); }
template <typename T> inline void chkmax(T &x , T y) { x = max(x , y); }
template <typename T> inline void read(T &x)
{
   T f = 1; x = 0;
   char c = getchar();
   for (; !isdigit(c); c = getchar()) if (c == -) f = -f;
   for (; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + c - 0;
   x *= f;
}
inline void build_sa()
{
    static int x[N] , y[N] , cnt[N];
    memset(cnt , 0 , sizeof(cnt));
    for (int i = 1; i <= len; i++) ++cnt[(int)s[i]];
    for (int i = 1; i <= 256; i++) cnt[i] += cnt[i - 1];
    for (int i = 1; i <= len; i++) sa[cnt[s[i]]--] = i;
    rk[sa[1]] = 1;
    for (int i = 2; i <= len; i++) rk[sa[i]] = rk[sa[i - 1]] + (s[sa[i]] != s[sa[i - 1]]);
    for (int k = 1; rk[sa[len]] != len; k <<= 1)
    {
        for (int i = 1; i <= len; i++)
            x[i] = rk[i] , y[i] = (i + k <= len) ? rk[i + k] : 0;
        memset(cnt , 0 , sizeof(cnt));
        for (int i = 1; i <= len; i++) ++cnt[y[i]];
        for (int i = 1; i <= len; i++) cnt[i] += cnt[i - 1];
        for (int i = len; i >= 1; i--) rk[cnt[y[i]]--] = i;
        memset(cnt , 0 , sizeof(cnt));
        for (int i = 1; i <= len; i++) ++cnt[x[i]];
        for (int i = 1; i <= len; i++) cnt[i] += cnt[i - 1];
        for (int i = len; i >= 1; i--) sa[cnt[x[rk[i]]]--] = rk[i];
        rk[sa[1]] = 1;
        for (int i = 2; i <= len; i++) rk[sa[i]] = (rk[sa[i - 1]]) + (x[sa[i]] != x[sa[i - 1]] || y[sa[i]] != y[sa[i - 1]]);
    } 
}
inline void get_height()
{
    int k = 0;
    for (int i = 1; i <= len; i++)
    {
        if (k) --k;
        int j = sa[rk[i] - 1];
        while (s[i + k] == s[j + k]) ++k;
        height[rk[i]] = k;
    }
}
inline void merge(info &x , info y)
{
    x = (info){x.height , x.cnta + y.cnta , x.cntb + y.cntb};
}
inline ll calc()
{
    int top = 0;
    static info s[N];
    ll ret = 0 , va = 0 , vb = 0;
    sa[0] = n + 1;
    for (int i = 1; i <= len; i++)
    {
        info tmp = (info){height[i] , sa[i - 1] < n + 1 , sa[i - 1] > n + 1};
        while (top > 0 && height[i] <= s[top].height)
        {
            va -= 1ll * s[top].height * s[top].cnta;
            vb -= 1ll * s[top].height * s[top].cntb;
            merge(tmp , s[top]);
            --top;
        }
        s[++top] = tmp;
        va += 1ll * s[top].height * s[top].cnta;
        vb += 1ll * s[top].height * s[top].cntb; 
        if (sa[i] < n + 1) ret += vb;
        else ret += va;
    }    
    return ret;
}

int main()
{
    
    scanf("%s%s" , s1 + 1 , s2 + 1);
    n = strlen(s1 + 1) ,
    m = strlen(s2 + 1);
    for (int i = 1; i <= n; i++) s[++len] = s1[i];
    s[++len] = #;
    for (int i = 1; i <= n; i++) s[++len] = s2[i];
    build_sa();
    get_height();
    printf("%lld
" , calc());
    
    return 0;
}

 

以上是关于[HAOI 2016] 找相同字符的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ4566:[Haoi2016]找相同字符

bzoj 4566 [Haoi2016]找相同字符SA

[HAOI2016]找相同字符(后缀数组+单调栈)

P3181 [HAOI2016]找相同字符

[HAOI2016]找相同字符(广义SAM)

[HAOI 2016] 找相同字符