給定一篇由若干個單詞構成的原文,還有乙個由若干個單詞組成的句子。加密文是由原文單詞通過某個單詞(可能一樣)替換而成的,原文相同單詞一定會被相同加密文單詞替換。沒有兩個不同的原文單詞被同乙個加密文單詞替換。
要求找出句子在加密文中第一次出現的位置。
原文本元總和不超過
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 ...