poj 3167 (kmp 好题)
2013-10-12 00:32
337 查看
题目:http://poj.org/problem?id=3167
题目大意:给你两个数列a、b,长度分别为 n 、k,让你用 b 去匹配 a,只要是两者的相对大小对的上就算匹配,让你求出所有的
匹配的位置。
思路:KMP好题!kmp 是顺序匹配的,建设前面已经匹配了,现在正在匹配 i 和 j ,因为是相对大小,如果i位置的前面比a[ i ]的
数目和 j 位置比b[ j ] 小的数目相等而且i位置的前面与a[ i ]相等的数目和 j 位置比b[ j ] 相等的数目相等,那么就是可以匹配。
把 while 里面相等的那句话换一下,直接kmp就好了。基本的思想就是这样,实现上面那个的方法貌似有很多种,由于s比较小,就直接
暴力上了。其他的明天再看,这里有个博客,说的不错:http://www.cppblog.com/zxb/archive/2010/10/06/128782.aspx?opt=admin,还有用树状数组统计排名的,也可以用枚举法。。。 = =
唉,这道题真是伤,一直WA,检查到半夜,终于发现,原来是把 while 写成 if 了,唉,无语啊。。。。 T^T
代码如下:
这个是树状数组版本,用树状数组看当前待匹配的值的排名,记当前匹配的是i和j+1,先把 i 这个值放进树状数组,看能不能匹配,如果
可以,那么这个值就插进去,不可以,记 len = j - fail[ j ],把 i 位置结尾的长度为 j+1 的字符串的前 len 个减掉。这里判断如果
j == k 时,也就是匹配找到一个答案时,也是要减掉这前面 len 个长度,不过前面开始是 i - j + 1,因为 j 之前 ++ 了,因为这个 WA
了几次。。。 = =
树状数组版本代码如下:
下面这个版本是根据上面那个博客来的,思路很好,代码如下:
还有一个枚举法,博客地址:http://www.mzry1992.com/blog/miao/%E3%80%90poj-3167%E3%80%91cow-patterns.html
依次递增枚举 p 中的数,然后匹配,整个过程都要保证匹配是递增的,复杂度O(s*s*n)。
好屌,这样都可以。。 这个方法,反正我是想不出来。。 = =
枚举代码如下:
题目大意:给你两个数列a、b,长度分别为 n 、k,让你用 b 去匹配 a,只要是两者的相对大小对的上就算匹配,让你求出所有的
匹配的位置。
思路:KMP好题!kmp 是顺序匹配的,建设前面已经匹配了,现在正在匹配 i 和 j ,因为是相对大小,如果i位置的前面比a[ i ]的
数目和 j 位置比b[ j ] 小的数目相等而且i位置的前面与a[ i ]相等的数目和 j 位置比b[ j ] 相等的数目相等,那么就是可以匹配。
把 while 里面相等的那句话换一下,直接kmp就好了。基本的思想就是这样,实现上面那个的方法貌似有很多种,由于s比较小,就直接
暴力上了。其他的明天再看,这里有个博客,说的不错:http://www.cppblog.com/zxb/archive/2010/10/06/128782.aspx?opt=admin,还有用树状数组统计排名的,也可以用枚举法。。。 = =
唉,这道题真是伤,一直WA,检查到半夜,终于发现,原来是把 while 写成 if 了,唉,无语啊。。。。 T^T
代码如下:
#include<cstdio> #include<cstring> #include<vector> #include<algorithm> using namespace std; int n,k,s; struct TT { int len; int num[111111]; int low[111111][33],equ[111111][33]; } t,p; void init(TT &tmp) { for(int i =1;i<=tmp.len;i++) { for(int j = 1;j<=s;j++) { tmp.low[i][j] = tmp.low[i-1][j] + (tmp.num[i] < j ? 1 : 0); tmp.equ[i][j] = tmp.equ[i-1][j] + (tmp.num[i] == j ? 1 : 0); } } } int fail[25555]; int check(TT &a,TT &b,int i,int j) { if(a.low[i][a.num[i]] - a.low[i - j][a.num[i]] == b.low[j][b.num[j]] && a.equ[i][a.num[i]] - a.equ[i - j][a.num[i]] == b.equ[j][b.num[j]]) return 1; return 0; } void get_fail() { fail[1] = 0; int j = 0; for(int i = 2;i<=k;i++) { if(j >= 1 && !check(p,p,i,j+1) ) j = fail[j]; if(check(p,p,i,j+1)) j++; fail[i] = j; } } vector <int> ans; void kmp() { ans.clear(); get_fail(); int j = 0; for(int i = 1;i<=n;i++) { while(j >= 1 && !check(t,p,i,j+1)) j = fail[j]; if(check(t,p,i,j+1)) j++; if(j == k) { ans.push_back(i-k+1); j = fail[j]; } } } int main() { while(~scanf("%d%d%d",&n,&k,&s)) { for(int i = 1;i<=n;i++) scanf("%d",&t.num[i]); for(int i = 1;i<=k;i++) scanf("%d",&p.num[i]); t.len = n; p.len = k; init(t); init(p); kmp(); printf("%d\n",ans.size()); for(int i = 0;i<ans.size();i++) printf("%d\n",ans[i]); } return 0; }
这个是树状数组版本,用树状数组看当前待匹配的值的排名,记当前匹配的是i和j+1,先把 i 这个值放进树状数组,看能不能匹配,如果
可以,那么这个值就插进去,不可以,记 len = j - fail[ j ],把 i 位置结尾的长度为 j+1 的字符串的前 len 个减掉。这里判断如果
j == k 时,也就是匹配找到一个答案时,也是要减掉这前面 len 个长度,不过前面开始是 i - j + 1,因为 j 之前 ++ 了,因为这个 WA
了几次。。。 = =
树状数组版本代码如下:
#include<cstdio> #include<cstring> #include<vector> #include<algorithm> using namespace std; int n,k,s; struct TT { int len; int num[111111]; int low[111111],equ[111111]; } t,p; int c[33]; int lowbit(int x) { return x&(-x); } void update(int x,int val) { while(x <= s) { c[x] += val; x += lowbit(x); } } int sum(int x) { int cnt = 0; while(x > 0) { cnt += c[x]; x -= lowbit(x); } return cnt; } void init(TT &tmp) { memset(c,0,sizeof(c)); for(int i =1;i<=tmp.len;i++) { update(tmp.num[i],1); tmp.low[i] = sum(tmp.num[i]-1); tmp.equ[i] = sum(tmp.num[i]) - sum(tmp.num[i]-1); //printf("i = %d,low = %d,equ = %d\n",i,tmp.low[i],tmp.equ[i]); } } int fail[25555]; void get_fail() { memset(c,0,sizeof(c)); fail[1] = 0; int j = 0; for(int i = 2;i<=k;i++) { update(p.num[i],1); while(j >= 1 && !(sum(p.num[i]-1) == p.low[j+1] && sum(p.num[i]) - sum(p.num[i] - 1) == p.equ[j+1])) { for(int kk = i - j;kk < i - fail[j];kk++) update(p.num[kk],-1); j = fail[j]; } if(sum(p.num[i]-1) == p.low[j+1] && sum(p.num[i]) - sum(p.num[i] - 1) == p.equ[j+1]) { j++; } else update(p.num[i],-1); fail[i] = j; //printf("i = %d,fail = %d\n",i,fail[i]); } } vector <int> ans; void kmp() { ans.clear(); get_fail(); int j = 0; memset(c,0,sizeof(c)); for(int i = 1;i<=n;i++) { update(t.num[i],1); while(j >= 1 && !(sum(t.num[i]-1) == p.low[j+1] && sum(t.num[i]) - sum(t.num[i] - 1) == p.equ[j+1])) { for(int kk = i - j ;kk < i - fail[j];kk++) update(t.num[kk],-1); j = fail[j]; } if(sum(t.num[i]-1) == p.low[j+1] && sum(t.num[i]) - sum(t.num[i] - 1) == p.equ[j+1]) { j++; } else update(t.num[i],-1); if(j == k) { ans.push_back(i-k+1); for(int kk = i - j + 1;kk <= i - fail[j];kk++) update(t.num[kk],-1); j = fail[j]; } } } int main() { while(~scanf("%d%d%d",&n,&k,&s)) { for(int i = 1;i<=n;i++) scanf("%d",&t.num[i]); for(int i = 1;i<=k;i++) scanf("%d",&p.num[i]); t.len = n; p.len = k; //init(t); init(p); kmp(); printf("%d\n",ans.size()); for(int i = 0;i<ans.size();i++) printf("%d\n",ans[i]); } return 0; }
下面这个版本是根据上面那个博客来的,思路很好,代码如下:
#include<cstdio> #include<cstring> #include<vector> #include<algorithm> using namespace std; int n,k,s; struct TT { int len; int num[111111]; int low[111111],occ[33],hig[111111]; } t,p; void init(TT &tmp) { memset(tmp.occ,-1,sizeof(tmp.occ)); tmp.occ[0] = -2; tmp.occ[s+1] = -2; for(int i =1;i<=tmp.len;i++) { if(tmp.occ[tmp.num[i]] == -1) tmp.occ[tmp.num[i]] = i; int j; for(j = tmp.num[i] - 1;tmp.occ[j] == -1;j--); tmp.low[i] = tmp.occ[j]; for(j = tmp.num[i] + 1;tmp.occ[j] == -1;j++); tmp.hig[i] = tmp.occ[j]; } } int check(int* a,int* b,int pos) { if(a[p.occ[b[pos]]] != a[pos]) return 0; if(p.low[pos] >= 0 && a[p.low[pos]] >= a[pos]) return 0; if(p.hig[pos] >= 0 && a[p.hig[pos]] <= a[pos]) return 0; return 1; } int fail[25555]; void get_fail() { fail[1] = 0; int j = 0; for(int i = 2;i<=k;i++) { while(j >= 1 && !check(p.num + (i - j - 1),p.num,j+1)) { j = fail[j]; } if(check(p.num + (i - j - 1),p.num,j+1)) { j++; } fail[i] = j; //printf("i = %d,fail = %d\n",i,fail[i]); } } vector <int> ans; void kmp() { ans.clear(); get_fail(); int j = 0; for(int i = 1;i<=n;i++) { while(j >= 1 && !check(t.num + (i - j - 1),p.num,j+1)) { j = fail[j]; } if(check(t.num + (i - j - 1),p.num,j+1)) { j++; } //printf("i = %d,j = %d\n",i,j); if(j == k) { ans.push_back(i-k+1); j = fail[j]; } } } int main() { while(~scanf("%d%d%d",&n,&k,&s)) { for(int i = 1;i<=n;i++) scanf("%d",&t.num[i]); for(int i = 1;i<=k;i++) scanf("%d",&p.num[i]); t.len = n; p.len = k; //init(t); init(p); kmp(); printf("%d\n",ans.size()); for(int i = 0;i<ans.size();i++) printf("%d\n",ans[i]); } return 0; }
还有一个枚举法,博客地址:http://www.mzry1992.com/blog/miao/%E3%80%90poj-3167%E3%80%91cow-patterns.html
依次递增枚举 p 中的数,然后匹配,整个过程都要保证匹配是递增的,复杂度O(s*s*n)。
好屌,这样都可以。。 这个方法,反正我是想不出来。。 = =
枚举代码如下:
#include<cstdio> #include<cstring> #include<vector> #include<algorithm> using namespace std; int n,k,s; int p[25005],t[111111]; int tmp_p[25005],tmp_t[111111]; int fail[25005]; void get_fail() { int j = 0; fail[1] = 0; for(int i = 2;i<=k;i++) { while(j >= 1 && tmp_p[i] != tmp_p[j+1]) j =fail[j]; if(tmp_p[i] == tmp_p[j+1]) j++; fail[i] = j; } } int tot; int cnt[111111],match[111111]; void kmp(int cur) { for(int i = 1;i<=n;i++) if(t[i] == cur) tmp_t[i] = 1; else tmp_t[i] = 0; int j = 0; for(int i = 1;i<=n;i++) { while(j >= 1 && tmp_t[i] != tmp_p[j+1]) j = fail[j]; if(tmp_t[i] == tmp_p[j+1]) j++; if(j == k) { int beg = i - j + 1; if(match[beg] < cur && cnt[beg] <= tot) { cnt[beg] ++; match[beg] = cur; } j = fail[j]; } } } vector <int> ans; int occ[33]; int main() { while(~scanf("%d%d%d",&n,&k,&s)) { memset(occ,0,sizeof(occ)); for(int i = 1;i<=n;i++) scanf("%d",&t[i]); for(int i = 1;i<=k;i++) scanf("%d",&p[i]),occ[p[i]] = 1; tot = 0; memset(match,0,sizeof(match)); memset(cnt,0,sizeof(cnt)); for(int i = 1;i<=s;i++) { if(!occ[i]) continue; for(int j = 1;j<=k;j++) if(p[j] == i) tmp_p[j] = 1; else tmp_p[j] = 0; get_fail(); for(int j = 1;j<=s;j++) kmp(j);// p 中 i 和 t 中的 j 相匹配 tot++; } ans.clear(); for(int i = 1;i<=n;i++) { //printf("i = %d,cnt = %d\n",i,cnt[i]); if(cnt[i] == tot) ans.push_back(i); } printf("%d\n",ans.size()); for(int i = 0;i<ans.size();i++) printf("%d\n",ans[i]); } return 0; }
相关文章推荐
- poj 3167 Cow Patterns(kmp)
- poj 3167 KMP好题
- Poj 3167 Cow Patterns Hdu 4749 Parade Show (KMP大小关系相同匹配+树状数组)
- POJ-3167- Cow Patterns(KMP)
- poj 3167 构造比较函数的kmp
- HDU 4749 && POJ 3167 KMP
- poj 3167 Cow Patterns kmp
- POJ 3167 Cow Pattern ★(KMP好题)
- POJ 3167 Cow Patterns (KMP+前缀和)
- POJ 3167 Cow Pattern ★(KMP好题)
- POJ 3167 Cow Patterns KMP+暴力
- HDU 4749 & POJ 3167 kmp变形
- POJ2752_Seek the Name, Seek the Fame_KMP_对next数组的运用
- POJ - 3450Corporate Identity(暴力kmp)
- poj2752——Seek the Name, Seek the Fame(KMP)
- HDU 3167 KMP
- poj 3461:Oulipo(KMP模版)
- POJ 3461 kmp
- Seek the Name, Seek the Fame POJ - 2752 KMP
- POJ 2752+KMP+利用next数组性质求出所有相同的前缀和后缀