NOI2016模擬5 14 最長公共子串行

2021-07-11 20:40:57 字數 3640 閱讀 5515

關於這個字首……是因為這個題目的名字很容易讓人想到一些別的題目,所以加字首區分一下。

設i表示a串不在lcs的位置,j表示b串不在lcs的位置。由於不能讓lcs結果為n,所以填入b串第j個位置的字元不能為s[j]。

我們先固定j,然後考慮b串的可能性個數。

考慮j<=i的情況。

第一條紅線前的部分就被確定了,只需要考慮第一條紅線後有多少種可能即可。

那麼就是看i取合法值時能造成的不同b串個數。

例如乙個i,假如存在乙個i′

<

i ,能造成相同的b串。

除兩條紅線以內的部分一定相同。現在紅線以內的部分也相同,那麼表明s[i』..i-1]=s[i』+1..i]也就是說s[i-1]=s[i]。

因此就很簡單了,合法的i一定滿足s[i-1]!=s[i]。然後列舉j並統計j之後有多少合法i,假設為t個,那麼此時貢獻為(m-1)*t,因為第j位有m-1種填法。

對於i<=j也同理,正著做一遍即可。然後減去i=j的情況。

**

#include

#include

#define fo(i,a,b) for(i=a;i<=b;i++)

#define fd(i,a,b) for(i=a;i>=b;i--)

using

namespace

std;

typedef

long

long ll;

const

int maxn=100000+10;

char a[maxn];

int i,j,k,l,t,n,m,cnt;

ll ans;

char get()

int main()

cnt=1;

fo(i,1,n)

ans-=(ll)(m-1)*n;

printf("%lld\n",ans);

}

這個做法為什麼會*****的拿了0分呢?

因為*****做法沒有考慮到當j不同時也可能造出一樣的b串。

乙個簡單的反例是ab。

接下來,我們來定義一種合法的方案:

1、去掉a串第i位與b串第j位後完全相同。

2、s[i]!=s[i+1]。(這條性質是因為如果滿足這個會存在i』與i重複,具體看上乙個部分)

3、b串第j位填入的字元不為s[j]。

4、用(i,j)來表示這種方案,暫不考慮你在b串第j位到底填了什麼。

然後我們假設當前有兩種合法方案重了,分別為(i』,i)和(j』,j),對應的b串分別為b1和b2。讓我們來**一下吧。

為了方便,我們設i<=j。

接下來我們來證明一些東西,下面列舉的是不可能的結論。

1、j』>i。

那麼b2的第i位必然是s[i],而根據性質3我們知道b1的第i位必然不是s[i]。這種情況下b1與b2不可能相同。

同理我們知道了i′

>

j 也是不可能的。 2、i

i 所以紅線後的部分是s[i+1..n] 因為j

′<=

i<=

j 即j′

<=

j 所以橙線後的部分是s[j+1..n]

因為b1和b2要相同所以藍線後的部分相同。

有s[j..n]=b2第j位+s[j+1..n]

那麼b2第j位即為s[j],違背了性質3。 因此i

因此我們知道j′

,所以我們可以知道b1在藍線前的部分是s[1..i-1] 因為j

′<=

j ,所以我們可以知道b2在藍線前的部分是s[1..j』-1]+s[j』+1..i]

那麼我們知道s[1..i-1]=s[1..j』-1]+s[j』+1..i]

也就是s[j』..i-1]=s[j』+1..i]

那麼就有s[j』]=s[j』+1],這與性質2產生了矛盾。

因此我們得到了j』=i,同理有i』=j。

然後就得到了出現重時只能是哪種情況。

接著我們設(i,j)和與(j,i)重,現在就是**重的條件。設

然後我們知道b2的第i位為s[i+1],因為b1與b2相同,b1的第i位是s[i+1],而b1的第i位也不能是s[i]。當s[i]=s[i+1]時,b1第i位一定不是s[i],也就一定不是s[i+1],那麼b1與b2就不相同,所以不符合要求。因此s[i]要不等於s[i+1]。

然後,紅線前的部分和藍線後的部分肯定相同。

考慮橙線到紫線這一段相同,即s[i..j-2]=s[i+2..j]

那麼就有

s[i]=s[i+2]

s[i+1]=s[i+3]

…… s[j-2]=s[j]

因此我們得到(i,j)會被算重的條件:

1、s[i]!=s[i+1]

2、s[i..j]是乙個形如ababababa……這樣的字串。

因此,我們只需要預處理right[i]表示每次往後跳兩格,跳到的必須與本身相同跳到的最遠點。

列舉i,然後便可以得到最大j,那麼s[i..j]中任一字首都會被算重。

#include

#include

#define fo(i,a,b) for(i=a;i<=b;i++)

#define fd(i,a,b) for(i=a;i>=b;i--)

using

namespace

std;

typedef

long

long ll;

const

int maxn=100000+10;

char a[maxn];

int right[maxn];

int i,j,k,l,t,n,m,cnt;

ll ans;

char get()

int main()

cnt=1;

fo(i,1,n)

ans-=(ll)(m-1)*n;

fo(i,1,n) right[i]=i;

fd(i,n-2,1)

if (a[i]==a[i+2]) right[i]=right[i+2];

fo(i,1,n-1)

printf("%lld\n",ans);

}

NOI2016 優秀的拆分

看到題目,資料範圍有點怪異。對於95 的資料,對於100 的資料,意思是只有5分是正解。好吧,95pts的 很明顯,答案就是 而如何才能拿到100pts呢?我們可以先列舉a段的長度,很明顯每個長度為lcp,與往後求lcs,若 這樣就可以通過 include include include inclu...

NOI2016 優秀的拆分

題目實際上要求我們求從每個點出發的aa串的數量 考慮點i的答案,發現如果字首i與字首j j i 的最長公共字尾 i j,那麼i點出發向前就存在乙個長度為i j的aa串,題目即求對於每個字首,有多少個在他之前的字首滿足條件 考慮字尾自動機,由於每個字首都是字尾自動機parent樹上的一點,即兩個字首的...

NOI2016 優秀的拆分

點此看題 首先轉化問題,我們可以求出a i b i a i b i a i b i 即以i ii結束 開始的aaaa aa串的數量,這樣答案就可以表示為 a i b i 1 sum a i times b i 1 a i b i 1 求這兩個陣列,可以隔距離len lenle n設定乙個點,這樣乙個...