【问题描述】
给定一个特定的序列,这个序列由数个单词组成,现在我们知道一个加密序列,已知加
密序列一定是特定序列的一部分,找出这个加密序列在特定序列中的第一次出现的位置。
注意:加密是以替换的形式,也就是说,加密序列与特定序列中的单词必须一一对应。
【输入格式】
第一行包括数个单词,单词末尾以$作结,每个单词间相隔一个空格,表示特定序列,$不被
算作单词。
第二行格式相同,表示加密序列。
注意:每一个序列的字母数不会超过 10^6,
【输出格式】
输出加密序列在特定序列中出现的第一个单词的位置,保证答案一定存在。
【输入输出样例】
输入
a a a b c d a b c $
x y $
输出
3
输入
a b c x c z z a b c $
prvi dr prvi tr tr x $
输出
3
输出
3
输入
xyz abc abc xyz $
abc abc $
输出
2
【数据说明】
对于 50%的数据,N≤1000。
对于 100%的数据,2≤N≤300000。
思路:
这题就是让我们在一个长串找一个相似的子结构,我们发现,虽然字符是不一样的,但是在同一个串中同一个字符的距离间隔应该是相同的,那么我们用距离来构造匹配串
以下面为例:
a b c x c z z a b c $
prvi dr prvi tr tr x $
生成串为:
0 0 0 0 2 0 1 7 7 5
0 0 2 0 1 0
然后我们会发现:
0 0 2 0 1 7
和
0 0 2 0 1 0
匹配了???
那是因为主串值可能被主串之前的值影响
那么有以下两点就是显然的了
1.本次的主串值在本次匹配中第一次出现,所以如果匹配,2号串的该位为0
2.因为是第一次出现,所以1号串的该位上代表的距离应该超出本次匹配串的长度
所以有:
while(j&&(s1[i+1]!=s2[j+1]&&(s2[j+1]>0||j-s1[i+1]>=0))) j=kmp[j];
if(s1[i+1]==s2[j+1]||(s2[j+1]<=0&&j-s1[i+1]<0)) j++;
然后就可以上代码了
#include <cstdio> #include <cstring> using namespace std; #define R register #define GC getchar() #define mod1 1000007 #define ll long long #define MAXN 1000000+5 #define mod2 10000000009 ll h[mod1+5]; int cnt1=0,cnt2=0; int s1[MAXN],s2[MAXN]; int kmp[MAXN],pl[mod1+5]; inline void pre(); inline void KMP(); inline char clear(); inline void KMP_pre(); int main() { pre(); KMP_pre(); KMP(); return 0; } inline char clear() { char c=GC; while (c<'a'||c>'z') c=GC; return c; } inline void pre() { freopen("word.in","r",stdin); freopen("word.out","w",stdout); R char c=clear(); while(c!='$') { R ll x=0; while(c>='a'&&c<='z') x=(x*37+(c-'a'+1))%mod2,c=GC; R ll tx=x%mod1;cnt1++; while(h[tx]) { if(h[tx]==x) { s1[cnt1]=cnt1-pl[tx]; //记录长度差 pl[tx]=cnt1;break; // 记录本次地址 } tx=(tx+1)%mod1; //处理哈希冲突 } if(!h[tx]) { s1[cnt1]=0; h[tx]=x,pl[tx]=cnt1; } c=GC; } memset(h,0,sizeof(h)); memset(pl,0,sizeof(pl)); c=clear(); while(c!='$') { R ll x=0; while(c>='a'&&c<='z') x=(x*37+(c-'a'+1))%mod2,c=GC; R ll tx=x%mod1;cnt2++; while(h[tx]) { if(h[tx]==x) { s2[cnt2]=cnt2-pl[tx];//同上 pl[tx]=cnt2;break; } tx=(tx+1)%mod1; } if(!h[tx]) { s2[cnt2]=0; h[tx]=x,pl[tx]=cnt2; } c=GC; } //for(R int i=1;i<=cnt1;i++) printf("%d ",s1[i]); //printf("\n"); //for(R int i=1;i<=cnt2;i++) printf("%d ",s2[i]); //printf("\n"); } inline void KMP_pre() { kmp[1]=0; for(R int i=1,j=0;i<cnt2;i++) { while(j&&s2[i+1]!=s2[j+1]) j=kmp[j]; if(s2[i+1]==s2[j+1]) j++; kmp[i+1]=j; } } inline void KMP() { for(R int i=0,j=0;i<cnt1;i++) { while(j&&(s1[i+1]!=s2[j+1]&&(s2[j+1]>0||j-s1[i+1]>=0))) j=kmp[j]; /*1.本次的主串值在本次匹配中第一次出现,所以如果匹配,2号串的该位为0 2.因为是第一次出现,所以1号串的该位上代表的距离应该超出本次匹配串的长度*/ if(s1[i+1]==s2[j+1]||(s2[j+1]<=0&&j-s1[i+1]<0)) j++; if(j==cnt2) {printf("%d\n",i+1-cnt2+1);return;} } putchar('0'),putchar('\n'); return; }