【问题描述】
给定一个特定的序列,这个序列由数个单词组成,现在我们知道一个加密序列,已知加
密序列一定是特定序列的一部分,找出这个加密序列在特定序列中的第一次出现的位置。
注意:加密是以替换的形式,也就是说,加密序列与特定序列中的单词必须一一对应。
【输入格式】
第一行包括数个单词,单词末尾以$作结,每个单词间相隔一个空格,表示特定序列,$不被
算作单词。
第二行格式相同,表示加密序列。
注意:每一个序列的字母数不会超过 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 }