【问题描述】
给定一个特定的序列,这个序列由数个单词组成,现在我们知道一个加密序列,已知加
密序列一定是特定序列的一部分,找出这个加密序列在特定序列中的第一次出现的位置。
注意:加密是以替换的形式,也就是说,加密序列与特定序列中的单词必须一一对应。
【输入格式】
第一行包括数个单词,单词末尾以$作结,每个单词间相隔一个空格,表示特定序列,$不被
算作单词。
第二行格式相同,表示加密序列。
注意:每一个序列的字母数不会超过 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++;
然后就可以上代码了
1 #include <cstdio> 2 #include <cstring> 3 4 using namespace std; 5 6 #define GC getchar() 7 #define R register 8 #define MAXN 1000000+5 9 #define mod1 838379 10 #define mod2 13578987531 11 #define ll long long 12 13 int s1[MAXN],s2[MAXN]; 14 int kmp[MAXN],pl[mod1+5]; 15 int cnt1=0,cnt2=0; 16 ll h[mod1+5]; 17 18 inline char clear(); 19 inline void pre(); 20 inline void KMP_pre(); 21 inline void KMP(); 22 23 int main() 24 { 25 pre(); 26 KMP_pre(); 27 KMP(); 28 return 0; 29 } 30 31 inline char clear() 32 { 33 char c=GC; 34 while (c<'a' || c>'z') c=GC; 35 return c; 36 } 37 38 inline void pre() 39 { 40 freopen("word.in","r",stdin); 41 freopen("word.out","w",stdout); 42 R char c=clear(); 43 while(c!='$') 44 { 45 R ll x=0; 46 while(c>='a'&&c<='z') x=(x*37+(c-'a'+1))%mod2,c=GC; 47 R ll tx=x%mod1;cnt1++; 48 while(h[tx]) 49 { 50 if(h[tx]==x) 51 { 52 s1[cnt1]=cnt1-pl[tx]; //记录长度差 53 pl[tx]=cnt1;break; // 记录本次地址 54 } 55 tx=(tx+1)%mod1; //处理哈希冲突 56 } 57 if(!h[tx]) 58 { 59 s1[cnt1]=0; 60 h[tx]=x,pl[tx]=cnt1; 61 } 62 c=GC; 63 } 64 memset(h,0,sizeof(h)); 65 memset(pl,0,sizeof(pl)); 66 c=clear(); 67 while(c!='$') 68 { 69 R ll x=0; 70 while(c>='a'&&c<='z') x=(x*37+(c-'a'+1))%mod2,c=GC; 71 R ll tx=x%mod1;cnt2++; 72 while(h[tx]) 73 { 74 if(h[tx]==x) 75 { 76 s2[cnt2]=cnt2-pl[tx];//同上 77 pl[tx]=cnt2;break; 78 } 79 tx=(tx+1)%mod1; 80 } 81 if(!h[tx]) 82 { 83 s2[cnt2]=0; 84 h[tx]=x,pl[tx]=cnt2; 85 } 86 c=GC; 87 } 88 89 //for(R int i=1;i<=cnt1;i++) printf("%d ",s1[i]); 90 //printf("\n"); 91 //for(R int i=1;i<=cnt2;i++) printf("%d ",s2[i]); 92 //printf("\n"); 93 } 94 95 inline void KMP_pre() 96 { 97 kmp[1]=0; 98 for(R int i=1,j=0;i<cnt2;i++) 99 { 100 while(j&&s2[i+1]!=s2[j+1]) j=kmp[j]; 101 if(s2[i+1]==s2[j+1]) j++; 102 kmp[i+1]=j; 103 } 104 } 105 106 inline void KMP() 107 { 108 for(R int i=0,j=0;i<cnt1;i++) 109 { 110 while(j&&(s1[i+1]!=s2[j+1]&&(s2[j+1]>0||j-s1[i+1]>=0))) j=kmp[j]; 111 /*1.本次的主串值在本次匹配中第一次出现,所以如果匹配,2号串的该位为0 112 2.因为是第一次出现,所以1号串的该位上代表的距离应该超出本次匹配串的长度*/ 113 if(s1[i+1]==s2[j+1]||(s2[j+1]<=0&&j-s1[i+1]<0)) j++; 114 if(j==cnt2) {printf("%d\n",i+1-cnt2+1);return;} 115 } 116 putchar('0'),putchar('\n'); 117 return; 118 }