• spoj 694 求一个字符串中不同子串的个数


    SPOJ Problem Set (classical)

    694. Distinct Substrings

    Problem code: DISUBSTR


    Given a string, we need to find the total number of its distinct substrings.

    Input

    T- number of test cases. T<=20;
    Each test case consists of one string, whose length is <= 1000

    Output

    For each test case output one number saying the number of distinct substrings.

    Example

    Sample Input:
    2
    CCCCC
    ABABA

    Sample Output:
    5
    9

    Explanation for the testcase with string ABABA: 
    len=1 : A,B
    len=2 : AB,BA
    len=3 : ABA,BAB
    len=4 : ABAB,BABA
    len=5 : ABABA
    Thus, total number of distinct substrings is 9.


    1、用前缀开看不同子串:能够看出--长度为i的字符串一共同拥有i个前缀

    2、每个子串都是某个后缀的前缀。于是问题等价于求全部不同的前缀的个数

    然后按sa[1],sa[2]...逐次增加后缀观察:
    suffix(sa[i])长度为n-sa[i],一共同拥有n-sa[i]个前缀,减去lcp[i-1](就是与前一个后缀的最长公共前缀的长度),就是新增加的新的前缀的个数

    最后求和就可以

    注意我的lcp[i]指的是suffix(sa[i])和suffix(sa[i+1])的公共前缀的长度

    #include <cstdio>
    #include <iostream>
    #include <string>
    #include <algorithm>
    #include <cmath>
    #include <cstring>
    
    using namespace std;
    #define MAXN 1011
    
    int n,k;//n=strlen(s);
    
    int Rank[MAXN];
    int tmp[MAXN];
    char s[MAXN];
    int lcp[MAXN],sa[MAXN];
    
    /*使用Rank对sa排序*/
    bool cmpSa(int i, int j)
    {
        if(Rank[i] != Rank[j])return Rank[i] < Rank[j];
        else
        {   /*以下的Rank[t]。已经是以t开头长度小于等于k/2的,
            sa[i]的名次。仅仅是以i开头的后缀。而长度不同*/
            int ri = i+k <=n? Rank[i+k]:-1;
            int rj = j+k <= n ? Rank[j+k]:-1;
            return ri <rj;
        }
    }
    
    /*计算SA*/
    void consa()
    {
        /*n=strlen(s);  必要时注明*/
        /*初始化sa和rank保证两点
            1、Rank[i]表示下标为i的是第几大,必须表示出相对大小。能够直接用字符代表其大小
            2、sa[1...n]值为1..n*/
        for(int i=0;i<=n;i++){
            sa[i]=i;Rank[i] = i < n?s[i]:-1;
        }
    
        /*利用长度为k的字符串对长度为2*k的字符串排序*/
        for(k=1;k<=n;k*=2)/*注意此代码中k是全局变量 别乱用,循环必须从1開始,由于0*2=0*/
        {
            sort(sa,sa+n+1,cmpSa);
            tmp[sa[0]] = 0; /*此时tmp仅仅是暂存rank*/
            for(int i=1;i<=n;i++){
                tmp[sa[i]] = tmp[sa[i-1]] +(cmpSa(sa[i-1],sa[i])?1:0);
                /*这一句非常关键,等号右側的sa[i]在此循环里表示第i大的长度小于等于k/2的字符串,
                  从而求出第i大的长度小于等于k的字符串的sa[i]*/
            }
            for(int i=0;i<=n;i++){
                Rank[i] = tmp[i];
            }
        }
    }
    
    void construct_lcp()
    {
        //n=strlen(s);
        for(int i=0; i<=n; i++)Rank[sa[i]]=i;
    
        int h=0;
        lcp[0]=0;
        for(int i=0;i<n;i++)
        {
            int j=sa[Rank[i]-1];
    
            if(h>0)h--;
            for(; j+h<n && i+h<n; h++)
            {
                if(s[j+h]!=s[i+h])break;
            }
            lcp[Rank[i]-1]=h;
        }
    }
    
    int main()
    {
        int t,ans;
        scanf("%d",&t);
        while(t--)
        {
            ans=0;
            scanf("%s",s);
            n=strlen(s);
            consa();
            construct_lcp();
            for(int i=1;i<=n;i++)
            {
                ans+=n-sa[i]-lcp[i-1];
            }
            printf("%d
    ",ans);
        }
        return 0;
    
        return 0;
    }
    



  • 相关阅读:
    30.过滤掉smb.conf配置文件中的空行和注释行和空白行(初级写法很不严谨)
    29. 分析文件内容,判断 fgets 共执行了多少次?
    28. 使用fgetc()/fputc()实现文件的加密与解密,存在溢出风险。
    27.读文件时通过两种方式判断文件结尾
    26. 使用fgetc()/fputc()实现文件的拷贝
    24. 练习定义几种指针及数组
    23. 实现 func()函数,在func()中,通过操作arr,实现修改str1到str2字符串,并打印所有字符串,考察对指针与数组与字符串的基本掌握
    Windows程序设计(Charles Petzold)HELLOWIN程序实现
    jvm 解释器和JIT编译器
    java 创建线程的方法
  • 原文地址:https://www.cnblogs.com/gavanwanggw/p/7293942.html
Copyright © 2020-2023  润新知