2013 ACM/ICPC 南京网络赛 1002 - Parade Show
10 5 102 4 2 4 2 4 2 4 2 41 2 1 2 1
1
-----------------KMP---------------
题目大意:
要从所给的长度为n的数字串A中,连续取出能够成为完美串的数字串,问最多有多少串 ?
如何称为完美串:给出一段长度为m的数字串B,他们的大小关系已确定,只要长度为n的数字串能够满足:
对于任意(i , j), 1<=i,j<=m ,若有 B[i]<B[j],则有A[i+k]<A[j+k];若有 B[i]=B[j],则有A[i+k]<A[j+k];若有 B[i]>B[j],则有A[i+k]>A[j+k] .
k是指A数字串是从k+1开始取的 .
我们考虑两个数字串A,B
A[1],A[2],…,A[k]与B[1],B[2],…,B[k]匹配条件:
若A[1],A[2],…,A[k-1]与B[1],B[2],…,B[k-1]匹配,则加上A[k]与B[k]仍然匹配的条件是:
必须在与k无关的时间复杂度内完成该操作(在这里,A是模式串,B是原串)
然而,由于A[1],A[2],…,A[k-1]与B[1],B[2],…,B[k-1]已经匹配,可以简化比较操作
考虑到数字集很小(不超过25),我们可以定义以下几个函数:
(注意理解)
Occur[p],Low[i],High[i]可能不存在,若存在则取最小的符合条件的x
则A[1],A[2],…,A[k-1]与B[1],B[2],…,B[k-1]匹配,加上A[k]与B[k]仍然匹配的条件可简化为:
接下来就可以用O(SK)时间内预处理求出Occur[p],Low[i],High[i],然后套用kmp算法,总时间复杂度为O(N+SK),具体实现时要注意Occur[p],Low[i],High[i]不存在的情况。
代码:
#include <map>#include <set>#include <list>#include <queue>#include <stack>#include <cmath>#include <ctime>#include <vector>#include <cstdio>#include <string>#include <numeric>#include <cstring>#include <iostream>#include <algorithm>#include <functional>using namespace std;typedef long long ll;typedef unsigned long long ull;#define eps 1e-8#define inf 0x7fffffff#define depug puts("pUG")#define lson l,m,rt<<1#define rson m+1,r,rt<<1|1#define Read freopen("in.txt","r",stdin)#define Write freopen("out.txt","w",stdout)#define Mem(a,x) memset(a,x,sizeof(a))#define maxn 555555int Hash[300],low[250100],high[250100];int next[maxn]= {-1};int s[maxn],p[maxn];int level;//这个变量实际上是没用的 , <=25实际上是提醒我们字符集很小,可以用很短的时间得到Low和Highint N;int n;int cont;//记录能分几段void init()//初始化{ cont=0; Mem(s,0); Mem(p,0); Mem(next,-1); Mem(Hash,-1);}bool read(){ init();//初始化 if(~scanf("%d%d%d",&N,&n,&level)) { Hash[0] = Hash[level + 1] = -2;//处理Hash键值不存在的情况 for(int i=0; i<N; i++) scanf("%d",&s[i]);//读入s串 for(int i=0; i<n; i++) { int j; scanf("%d",&p[i]);//读入p串 if(Hash[p[i]] == -1)//当这个值第一次出现 Hash[p[i]]= i; for(j = p[i] - 1; Hash[j] == -1; j--); low[i] = Hash[j]; //寻找当前数字之前第一个小于当前数字的数字在p串的位置(不存在则为-1) for(j = p[i] + 1; Hash[j] == -1; j++); high[i] = Hash[j];//寻找当前数字之后第一个大于当前数字的数字在p串的位置(不存在则为-1) } return 1; } return 0;}bool cmp(int *a, int *b, int k)//A为匹配串,B为原串{ if(b[Hash[a[k]]] != b[k]) return false; if(low[k] >= 0 &&b[low[k]] >= b[k]) return false; if(high[k]>= 0 &&b[high[k]]<= b[k]) return false; return true;}void GetNext()//next[maxn] p[maxn]{ int j=0,k=-1; next[0]=-1; while(j<n) { if(k==-1|| cmp(p,p+j-k,k))//自身匹配 { k++; j++; next[j]=k; } else k=next[k]; }}int KMP(int s0){ int j=s0,k=0; while(j<N&&k<n) { if(k==-1||cmp(p,s+j-k,k))//原串与匹配串匹配 j++,k++; else k=next[k]; } if(k>=n) return j;//返回匹配成功后的位置 else return -1;}int main(){ while(read()) { int len=0; GetNext(); while(len<N) { int temp=KMP(len); if(temp>=0) cont++,len=temp;//从成功的位置后继续匹配 else break;//跳出 } printf("%d\n",cont); } return 0;}