codeforces D.神祕的犯罪 + n個字串的公共子串+技巧
在m組數,每組有n個數(數的範圍1-n)中,找到某些序列 使它是每組數的一個公共子序列,問這樣的某些序列的個數?
1≤n≤100000 , 1≤m≤10
公共子串:1 2 3 23一共4個。
思考:我當時想的是暴力搜尋一遍,找到1,2序列的公共子串(長度>1)在去3~n序列搜尋
例如:
5 2
1 2 3 4 5
1 5 2 3 4
找到了234,因為單獨的一個數組肯定是一個公共子串,所以只考慮>1的公共子串計數
對234長度為2的公共子串為2, 長度為3為1
那麼一共有5 + 2 + 1=8
對長度為n的公共子串有(n-1)+(n-2)+(n-3)+…+1
看複雜度好像是可行的,最大O(nm),但是比較難寫。
看了大佬的題解,果然用了一個方法比較快速又方便的方法找到n個字串的公共子串
創立一個next陣列,使每組中第i個數的next 是第i+1個數,即 nex[ a[i] ] = a[ i+1 ] (實際上設next是二維陣列)。對第一組中的第i個數,如果在其餘每組的next[ a[ i -1] ]都是等於第一組中a[ i ]的,意味著序列 a[ i-1 ],a[ i ]是一個公共子序列
這個方法太神了,因為可以在複雜度為O(1)的情況下,找到其他串應該和這個字元匹配的位置,看不懂的自己慢慢理解。
利用一個數組 d[ ],d[ i ]記做 第 i 個數到第1個數之間滿足條件的子序列的個數 。對 i ,如果滿足條件,是公共子序列的話,d[ i ]=d[ i-1 ] + d[ i ],當然初始的時候 d[ i ] =1 (因為單獨的一個數組肯定是一個公共子串,題目一定滿足)。
這個d[i]可以擴充套件一下:
如果第i個字元匹配假設長度為n
對長度為n的公共子串有(n)+(n-1)+(n-2)+…+1(單個數計入)
那麼如果i+1個字元也匹配,長度為n+1
(n+1)+(n)+(n-1)+…+1
所以增加了(n+1)
因為d[i]=n,上面定義,d[i+1]+=d[i],所以d[i+1]=n+1正好為增加的公共子串,所以把所有的d[]相加就是答案,而且d[]的最大值就為最長的公共公共子串,還可以想想什麼時候d[]初始化為0
思考:用了一節大物課學了個神仙套路,賺了
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=100005; int n,m,a[11][N]; int nex[11][N]; ll d[N]; int main() { a[1][0]=0; cin>>n>>m; for(int i=1;i<=m;i++) { for(int j=1;j<=n;j++) { scanf("%d",&a[i][j]); nex[i][a[i][j-1]]=a[i][j]; } } d[1]=1; for(int i=2;i<=n;i++) { int x=a[1][i-1]; int flag=0; d[i]=1; for(int j=2;j<=m;j++) { if(a[1][i] != nex[j][x] ){ flag=1; break; } } if(!flag) d[i]+= d[i-1]; } ll ans=0; for(int i=1;i<=n;i++) { ans+=d[i]; } cout<<ans<<' '<<*max_element(d+1, d+n+1)<<endl; } /* 5 6 1 2 3 4 5 2 3 1 4 5 3 4 5 1 2 3 5 4 2 1 2 3 5 4 1 1 2 3 4 5 */