1. 程式人生 > >Codeforces Round#519 D. Mysterious Crime【想法題】

Codeforces Round#519 D. Mysterious Crime【想法題】

【題意】:給你m個長度為n的排列,m<=10,n<=100000,讓你求有多少種不同的公共子串。

【題解】:就目前而言,如果是在多個字串中求不同的公共子串個數,還不是一個能在較低複雜度下就能解決的問題,所以這道題一定是有它自己特殊的地方:排列(permutaion)。因為是排列,那麼每一個數字只會出現一次,那我們就能記錄每一個字串中每一個數字後面跟著的是誰,那麼判斷是否存在某個子串的時候,只要O(1)的時間就可以知道是不是存在。

比如,判斷1 2是否是所有串的公共子串。那麼我們首先應該判斷好1已經是所有串的公共子串,因為一個串如果是所有串的公共子串,那它的任何子串也都必須是所有串的公共子串。那麼我們在每個串中用O(1)的時間判斷一下1後面跟著的是不是2即可。

所以我們可以在第一個串中尺取。如果當前數字滿足條件,右指標往右走,不滿足的時候 左指標到右指標,右指標不動,並且ans+=len*(len+1)/2,len是當前的串長度,即r-l+1。

【程式碼】:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int num[15][100000+10];
int hou[15][100000+10];
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=0;i<m;i++){
        for(int j=0;j<n;j++){
            scanf("%d",&num[i][j]);
            if(j>0) hou[i][num[i][j-1]]=num[i][j];
        }
    }
    if(m==1){
        ll ans=(ll)n*(n+1)/2;
        return 0*printf("%lld\n",ans);
    }
    ll ans=0;
    int l=0,r=0,len=0,be=0;
    for(;r<n;r++){
        //printf("%d %d\n",l,r);
        if(l==r){
            len=1;
            be=r;
        }
        else{
            int flag=0;
            for(int j=1;j<m;j++){
                if(hou[j][num[0][be]]!=num[0][r]){
                    flag=1;
                    break;
                }
            }
            if(flag==0){
                be=r;
                len++;
            }
            else{
                ans+=(ll)len*(len+1)/2;
                len=0;
                l=r;
                r--;
            }
        }
    }
    if(len){
        ans+=(ll)len*(len+1)/2;
    }
    printf("%lld\n",ans);
    return 0;
}