2025年P2408 不同子串个数

P2408 不同子串个数P2408 不同子串个数 题意 给你一个长为 n 的字符串 求不同的子串的个数 我们定义两个子串不同 当且仅当有这两个子串长度不一样或者长度一样且有任意一位不一样 子串的定义 原字符串中连续的一段字符组成的字符串 题解 对于任何一个字串

大家好,我是讯享网,很高兴认识大家。

P2408 不同子串个数

题意:

给你一个长为 n 的字符串,求不同的子串的个数。

我们定义两个子串不同,当且仅当有这两个子串长度不一样或者长度一样且有任意一位不一样。

子串的定义:原字符串中连续的一段字符组成的字符串。


讯享网

题解:

对于任何一个字串,一定是一个后缀的前缀,所以我们可以求出用后缀数组求出LCP(最长公共前缀),再求出每个后缀对答案的贡献。
一个长度为n的字符串,产生的字符串的个数是n(n+1)/2个,这是总数
现在我们开始考虑重复的情况:

height[i]就是2,最前面两个aa是都有的,是重复的,说明这个重复个数正好就是height[i]
但是后面重复情况(bbdbs部分是重复)呀?但其实没关系,因为我们处理了所有的后缀,总会有一个情况处理了bbdbs这情况。这样我们求出整个串中重复的串的个数,就是 ∑ i = 1 n h e i g h t [ i ] \sum_{i=1}^{n}height[i] i=1nheight[i]
答案就是:n(n+1)/2- ∑ i = 1 n h e i g h t [ i ] \sum_{i=1}^{n}height[i] i=1nheight[i]
记得开ll

代码:

// Problem: P2408 不同子串个数 // Contest: Luogu // URL: https://www.luogu.com.cn/problem/P2408 // Memory Limit: 125 MB // Time Limit: 1000 ms // Data:2021-08-22 12:24:46 // By Jozky #include <bits/stdc++.h> #include <unordered_map> #define debug(a, b) printf("%s = %d\n", a, b); using namespace std; typedef long long ll; typedef unsigned long long ull; typedef pair<int, int> PII; clock_t startTime, endTime; //Fe~Jozky const ll INF_ll= 1e18; const int INF_int= 0x3f3f3f3f; void read(){ 
   }; template <typename _Tp, typename... _Tps> void read(_Tp& x, _Tps&... Ar) { 
    x= 0; char c= getchar(); bool flag= 0; while (c < '0' || c > '9') flag|= (c == '-'), c= getchar(); while (c >= '0' && c <= '9') x= (x << 3) + (x << 1) + (c ^ 48), c= getchar(); if (flag) x= -x; read(Ar...); } template <typename T> inline void write(T x) { 
    if (x < 0) { 
    x= ~(x - 1); putchar('-'); } if (x > 9) write(x / 10); putchar(x % 10 + '0'); } void rd_test() { 
    #ifdef LOCAL startTime= clock(); freopen("in.txt", "r", stdin); #endif } void Time_test() { 
    #ifdef LOCAL endTime= clock(); printf("\nRun Time:%lfs\n", (double)(endTime - startTime) / CLOCKS_PER_SEC); #endif } const int MAXN= ; char ch[MAXN], all[MAXN]; int sa[MAXN], rk[MAXN], height[MAXN], tax[MAXN], tp[MAXN], a[MAXN], n, m; char str[MAXN]; //rk[i] 第i个后缀的排名; sa[i] 排名为i的后缀位置; height[i] 排名为i的后缀与排名为(i-1)的后缀的LCP //tax[i] 计数排序辅助数组; tp[i] rk的辅助数组(计数排序中的第二关键字),与sa意义一样。 //a为原串 void RSort() { 
    //rk第一关键字,tp第二关键字。 for (int i= 0; i <= m; i++) tax[i]= 0; for (int i= 1; i <= n; i++) tax[rk[tp[i]]]++; for (int i= 1; i <= m; i++) tax[i]+= tax[i - 1]; for (int i= n; i >= 1; i--) sa[tax[rk[tp[i]]]--]= tp[i]; //确保满足第一关键字的同时,再满足第二关键字的要求 } //计数排序,把新的二元组排序。 int cmp(int* f, int x, int y, int w) { 
    return f[x] == f[y] && f[x + w] == f[y + w]; } //通过二元组两个下标的比较,确定两个子串是否相同 void Suffix() { 
    //sa for (int i= 1; i <= n; i++) rk[i]= a[i], tp[i]= i; m= 127, RSort(); //一开始是以单个字符为单位,所以(m = 127) for (int w= 1, p= 1, i; p < n; w+= w, m= p) { 
    //把子串长度翻倍,更新rk //w 当前一个子串的长度; m 当前离散后的排名种类数 //当前的tp(第二关键字)可直接由上一次的sa的得到 for (p= 0, i= n - w + 1; i <= n; i++) tp[++p]= i; //长度越界,第二关键字为0 for (i= 1; i <= n; i++) if (sa[i] > w) tp[++p]= sa[i] - w; //更新sa值,并用tp暂时存下上一轮的rk(用于cmp比较) RSort(), swap(rk, tp), rk[sa[1]]= p= 1; //用已经完成的sa来更新与它互逆的rk,并离散rk for (i= 2; i <= n; i++) rk[sa[i]]= cmp(tp, sa[i], sa[i - 1], w) ? p : ++p; } //离散:把相等的字符串的rk设为相同。 //LCP int j, k= 0; for (int i= 1; i <= n; height[rk[i++]]= k) for (k= k ? k - 1 : k, j= sa[rk[i] - 1]; a[i + k] == a[j + k]; ++k) ; //这个知道原理后就比较好理解程序 } void Init() { 
    int t; read(t); scanf("%s", str); // cout << str << endl; n= strlen(str); for (int i= 0; i < n; i++) a[i + 1]= str[i]; } int main() { 
    //rd_test(); Init(); Suffix(); ll ans= 0; // for (int i= 1; i <= n; i++) // cout << "hei[]=" << height[i] << endl; for (int i= 1; i <= n; i++) { 
    ans+= 1ll * (n - i + 1) - 1ll * (height[sa[i]]); } cout << ans; // for (int i= 1; i <= n; i++) { 
    // cout << "rank[i]=" << rk[i] << endl; // } // for (int i= 1; i <= n; i++) { 
    // cout << "hei[i]=" << height[rk[i]] << endl; // } /* aabaa abaa baa aa a a aa aabaa abaa baa */ //Time_test(); } 

讯享网
小讯
上一篇 2025-02-22 12:19
下一篇 2025-03-21 12:00

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/63625.html