GDOI模擬2015 08 18 解密

2021-07-04 19:04:19 字數 3148 閱讀 3362

給定一篇由若干個單詞構成的原文,還有乙個由若干個單詞組成的句子。加密文是由原文單詞通過某個單詞(可能一樣)替換而成的,原文相同單詞一定會被相同加密文單詞替換。沒有兩個不同的原文單詞被同乙個加密文單詞替換。

要求找出句子在加密文中第一次出現的位置。

原文本元總和不超過

1000000

,句子字元總和不超過

1000000

。所有單詞由若干小寫字母組成。

這題一看就大概知道是字串的模式匹配問題。正解是用最小表示法表示字串,然後上kmp或是hash。

我比賽時想了乙個比較另類的方法。我們將原文第

i 個單詞通過乙個方法表示:su

cc0,

i表示第

i 個原文中的單詞距離下乙個一樣的單詞(後繼)的距離,如果後面沒有就為

0。句子也用相同方法表示(句子中後繼的距離)為su

cc1,

i 。這種表示法能排除單詞替換的影響,表示出一連串單詞的性質了。

這個時候能否直接上kmp演算法呢?我們可以發現乙個很顯然的反例。記原文為ar

ticl

e ,句子為se

nten

ce。假設我們句子要匹配文章第

i 個單詞開始的一連串單詞,中間有乙個j(

i≤jleng

th(s

ente

nce)

),滿足su

cc0,

j+j≥

i+le

ngth

(sen

tenc

e)。這時一般匹配會判斷兩串第j−

i+1 位不等,但是實際上,ar

ticl

ej的後繼已經超過了比較範圍,對答案沒有影響。所以,匹配的開始位置會影響每個單詞的表示。也就是當原文第

i 個單詞在匹配範圍內有後繼時,它表示為su

cc0,

i,否則表示為

0 。

這種能模式串隨匹配串位置改變的kmp我沒有yy出來,於是我打了個hash。我們發現,如果我們順序列舉匹配位置,每個原文單詞值最多會變化兩次(從0變為

succ

0,i )。所以我們可以將原文每個

i 用模擬鍊錶之類的東西掛在i+

succ

0,i的位置上。預處理句子的雜湊值,然後從左到右列舉匹配位置,同時處理當前雜湊值,單詞值變化的處理,只需對於匹配位置最右端掛著的位置,將雜湊數中相應位置加上相應的雜湊值即可。

感覺講得很亂,不懂的就看看**實現吧。處理後繼su

cc我打了個tr

ie,空間卡得很艱難(題目給的空間也太™小了)。

#include 

#include

#include

#include

#include

using namespace std;

typedef long long ll;

const int n=1000000;

int seed[4][3]=,,,};

char article[n+1],sentence[n+1];

int aword[n+2],sword[n+2];

int power[3][n+1];

int succ[2][n+1];

char temp[n+1];

int sthash[3];

int prime[4];

int n,m,ans,as,ss;

struct trie

void insert(int len,int

pos,int kind)

i=next[i];

}if (found)

rt=y;

else

}if (key[rt])

succ[kind][key[rt]]=pos-key[rt];

key[rt]=pos;

}}trie;

void hang(int

x,int

y)void read()

if (ch>='a'&&ch<='z')

la=n;

while (ch>='a'&&ch<='z')

}aword[as+1]=n;

article[n]='\0';

ch=getchar();

la=0;

while (ch!='$')

if (ch>='a'&&ch<='z')

la=m;

while (ch>='a'&&ch<='z')

}sword[ss+1]=m;

sentence[m]='\0';

}void preparation()

trie.init();

for (int i=1;i<=ss;i++)

trie.init();

for (int i=1;i<=as;i++)

if (succ[0][i])

hang(i+succ[0][i],i);

srand(time(0));

for (int i=0;i<4;i++)

prime[i]=seed[i][rand()%3];

for (int i=0;i<3;i++)

}void solve()

,item,ptr;

for (int i=1;i<=ss-1;i++)

for (int j=0;j<3;j++)

}int la=1,tmp;

for (int i=ss;i<=as;i++)

}if (hash[0]==sthash[0]&&hash[1]==sthash[1]&&hash[2]==sthash[2])

tmp=succ[0][i-ss+1]+i-ss+1>i?0:succ[0][i-ss+1];

for (int j=0;j<3;j++)

hash[j]=(((ll)hash[j]-((ll)tmp*power[j][ss-1]%prime[3]))%prime[3]+prime[3])%prime[3];

}}int main()

看到這題,其實大家都想到什麼。當然,就是oj的**相似度判斷。我腦補了一下,oj其實可以將**縮排回車空格刪除,拆成若干表示式和句子,然後用這題演算法的改進版來判斷相似度(大神勿噴)。

GDOI模擬 排列

給你m個對1到n的排列的特徵,特徵有兩種 1 x y v 排列的第x個數到第y個數之間的最大值為v 2 x y v 排列的第x個數到第y個數之間的最小值為v 要求你還原出這個排列。刷水有益身心健康。既然是求方案,資料範圍又很小,那麼明顯的要用把點向權值連邊。然後他每次給出範圍之後再進行刪邊。最後,二...

GDOI模擬8 21總結

今天做了cqoi2013的題。第一次5個小時做5道題 先看了半小時的題。t5是初中做過的原題,記得是處理出上下界然後暴力列舉就行了,就先打了t5,花了乙個小時搞定了t5,過了樣例和自己出的幾個資料就沒管了 沒對拍是因為我覺得資料生成器有點難搞 然後這題做法本來就是暴力 然後去看了下之前沒怎麼看懂題的...

GDOI模擬4 15 4 17總結

day1 題目都不會做。所以打完暴力開始優化暴力,結果有乙個程式優化錯了,另乙個 暴力的時候多打了乙個符號爆零了。在看第一題的時候其實是想到了正解,但是看時間有點大不敢打。其實打了可能也有60分的,賽後才知道。這個方法常數卡的好可以拿100分。結果第一天就只拿了100分。理論上第一天 60 100 ...