New Distinct Substrings (后缀数组,统计有多少个不同的子串)

本文详细介绍了如何使用后缀数组和高度数组来解决字符串处理中的子串计数问题。通过实例说明了height数组的计算方法,并给出了完整的C++实现代码。

首先要确认一个事实:任何一个子串都是一个后缀的前缀。
根据这个,首先处理出height数组。

对于样例:ABABA
后缀数组处理出来是这样的结果。
A 0
ABA 1
ABABA 3
BA 0
BABA 2

右边的数字是height数组的值。

因为:任何一个子串都是一个后缀的前缀。 所以对于每个后缀就有 n-sa[i]个子串。
这时候从第一个后缀出来的答案是1。
然后看第二个,height是1,意味着A是和上面那个重复的,所以在计算这个的子串的时候,我们需要在这个基础上,把这个多算的A个减去。
第三个与第二个同理,height是3,意味着ABA都是重复的,所以需要减去3(ABA,AB,A 这3个多出来了)。
这个A看上去减了2遍,事实上这样做答案才会正确。因为既然这个height大于1了,就意味着第一个字符是重复的,而这个重复的字符一定在上面被算过了,所以不需要再计算。

最后的答案就是 把每个的 n-sa[i]-height[i]加了起来。

(代码中是n-1-sa[i]-height[i],这是因为对于后缀数组模板的修正,后面加一个比一切字符都小的字符)。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
const int nMax = 1200001;


char arr[nMax+1];
int sa[nMax], rank[nMax], height[nMax];
int wa[nMax], wb[nMax], wv[nMax], wd[nMax];
int n,k;
int cmp(int *r, int a, int b, int l){
    return r[a] == r[b] && r[a+l] == r[b+l];
}

void da(char *r, int n, int m){          //  倍增算法 r为待匹配数组  n为总长度 m为字符范围
    int i, j, p, *x = wa, *y = wb, *t;
    for(i = 0; i < m; i ++) wd[i] = 0;
    for(i = 0; i < n; i ++) wd[x[i]=r[i]] ++;
    for(i = 1; i < m; i ++) wd[i] += wd[i-1];
    for(i = n-1; i >= 0; i --) sa[-- wd[x[i]]] = i;
    for(j = 1, p = 1; p < n; j *= 2, m = p){
        for(p = 0, i = n-j; i < n; i ++) y[p ++] = i;
        for(i = 0; i < n; i ++) if(sa[i] >= j) y[p ++] = sa[i] - j;
        for(i = 0; i < n; i ++) wv[i] = x[y[i]];
        for(i = 0; i < m; i ++) wd[i] = 0;
        for(i = 0; i < n; i ++) wd[wv[i]] ++;
        for(i = 1; i < m; i ++) wd[i] += wd[i-1];
        for(i = n-1; i >= 0; i --) sa[-- wd[wv[i]]] = y[i];
        for(t = x, x = y, y = t, p = 1, x[sa[0]] = 0, i = 1; i < n; i ++){
            x[sa[i]] = cmp(y, sa[i-1], sa[i], j) ? p - 1: p ++;
        }
    }
}

void calHeight(char *r, int n){           //  求height数组。
    int i, j, k = 0;
    for(i = 1; i <= n; i ++)
    {
        rank[sa[i]] = i;
        //cout<<sa[i]<<endl;
    }
    for(i = 0; i < n; height[rank[i ++]] = k){
        for(k ? k -- : 0, j = sa[rank[i]-1]; r[i+k] == r[j+k]; k ++);
    }
}



int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        scanf("%s",arr);
        int len=strlen(arr);
        arr[len]=0;
        arr[len+1]='\0';
        len++;

        da(arr,len,140);
        calHeight(arr,len-1);

        int ans=0;
        for(int i=1;i<=len-1;i++)
        {
            ans+=len-1-sa[i]-height[i];
        }
        cout<<ans<<endl;
    }
    return 0;
}
统计一个对象数组中有多少 **不同的 `playId`** 条目(即去重后的数量),可以使用多种编程语言实现。以下是几种常见语言和环境下的解决方案。 --- ### ✅ 示例数据(JavaScript) ```js const data = [ { playId: 'p1', name: 'video1' }, { playId: 'p2', name: 'video2' }, { playId: 'p1', name: 'video3' }, { playId: 'p3', name: 'video4' }, { playId: 'p2', name: 'video5' } ]; ``` 我们希望得到:**3**(因为 `playId` 去重后是 `'p1', 'p2', 'p3'`) --- ## 🧩 方法一:JavaScript 实现 ```javascript function countUniquePlayIds(arr) { const uniquePlayIds = new Set(arr.map(item => item.playId)); return uniquePlayIds.size; } // 使用示例: const count = countUniquePlayIds(data); console.log(count); // 输出: 3 ``` ### 🔍 解释: - `arr.map(item => item.playId)`:提取所有 `playId` - `new Set(...)`:自动去重 - `.size`:获取唯一值的数量 --- ## 🧩 方法二:使用 `reduce`(兼容老版本或复杂逻辑) ```javascript function countUniquePlayIds(arr) { const seen = {}; arr.forEach(item => { if (item.playId !== undefined) { seen[item.playId] = true; } }); return Object.keys(seen).length; } ``` --- ## 🧩 方法三:Python 实现(如用 pandas 或普通 list) ### 普通 Python 列表: ```python data = [ {'playId': 'p1', 'name': 'video1'}, {'playId': 'p2', 'name': 'video2'}, {'playId': 'p1', 'name': 'video3'}, {'playId': 'p3', 'name': 'video4'} ] unique_count = len(set(item['playId'] for item in data)) print(unique_count) # 输出: 3 ``` ### 如果是 Pandas DataFrame: ```python import pandas as pd df = pd.DataFrame(data) count = df['playId'].nunique() # nunique 自动去重计数 print(count) ``` --- ## 🧩 方法四:SQL 查询等价写法 如果你的数据在数据库中,比如一张表 `video_events(playId, ...)`: ```sql SELECT COUNT(DISTINCT playId) AS unique_play_id_count FROM video_events; ``` --- ### ✅ 总结 | 方法 | 语言/环境 | 代码简洁性 | 适用场景 | |------|-----------|------------|---------| | `Set + map` | JavaScript | ⭐⭐⭐⭐☆ | 前端、Node.js | | `set comprehension` | Python | ⭐⭐⭐⭐⭐ | 数据处理脚本 | | `nunique()` | Pandas | ⭐⭐⭐⭐☆ | 数据分析 | | `COUNT(DISTINCT)` | SQL | ⭐⭐⭐⭐⭐ | 数据库统计 | ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值