单词(word)

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

猜你喜欢

转载自www.cnblogs.com/000226wrp/p/11347856.html