poj 3167 Cow Patterns ... 四种算法 (均结合kmp)
来源:互联网 发布:东华软件数据库 编辑:程序博客网 时间:2024/05/20 05:58
题目链接:http://poj.org/problem?id=3167
第1,2种算法思路是一样的,但是实现方法不同。
算法归于以下结论:两个序列的偏序相同则两序列同一位以前小于它和等于它的数的数量应该是相同的。
比如:题目匹配的结果是: 2 10 10 7 3 2
1 4 4 3 2 1 --- >>> 说不清楚^_^...
现在的目的就是找出如何快速的算出一个序列每一位前面有多少元素小于它,多少元素等于它?
第一种方法:树状数组。同时理解kmp匹配时候总是按非递减的数量舍弃前面不能匹配的值。 这样可以用一个TreeArray来保存舍弃的值。然后减去即可。主要还是看代码吧。
第二种方法:因为s很小,所以可以直接开一个数组保存每一位前面每种元素出现的次数。暴力 + kmp。
1:树状数组实现
//Problem: 3167User: _missing//Memory: 2040KTime: 313MS//Language: C++Result: Accepted#include <iostream>#include <cstdio>#include <cstring>using namespace std;const int MAXN = 100000 + 1000;struct TreeArray{ int N, *C; TreeArray(int N =0, int *C =0): N(N), C(C) { Init(); } void Init() { memset(C, 0, sizeof(int)*(N+1)); } int lowbit(int x) { return x&(-x); } void Add(int x, int v) { while (x <= N) { C[x] += v; x += lowbit(x); } } int Sum(int x) { int res =0; while (x > 0) { res += C[x]; x -= lowbit(x); } return res; } int Equ(int x) { return Sum(x)-Sum(x-1); } int Low(int x) { return Sum(x-1); }};struct Cow{ int num[MAXN]; int low[MAXN]; int equ[MAXN];};Cow cow, pot;int nxt[MAXN];int ans[MAXN];int arr[30];int n, k, s;void Init(Cow &cow, int n){ TreeArray ta(s, arr); for ( int i =1; i <= n; i++) { ta.Add(cow.num[i], 1); cow.low[i] = ta.Low(cow.num[i]); cow.equ[i] = ta.Equ(cow.num[i]); } //for ( int i =1; i <= n; i++) printf("%d ", cow.low[i]); cout << endl; //for ( int i =1; i <= n; i++) printf("%d ", cow.equ[i]); cout << endl; //cout << endl;}void GetNext(){ TreeArray sub(s, arr); sub.Add(pot.num[1], 1); nxt[1] = 0; for ( int i =2, j =0; i <= k; i++) { while (j>0 && (pot.low[j+1] != pot.low[i]-sub.Low(pot.num[i]) || pot.equ[j+1] != pot.equ[i]-sub.Equ(pot.num[i]))) { for ( int k =i-j; k < i-nxt[j]; k++) sub.Add(pot.num[k], 1); j = nxt[j]; } if ( pot.low[j+1] == pot.low[i]-sub.Low(pot.num[i]) && pot.equ[j+1] == pot.equ[i]-sub.Equ(pot.num[i]) ) ++j; else sub.Add(pot.num[i], 1); nxt[i] = j; } //for ( int i =1; i <= k; i++) printf("%d ", nxt[i]); cout << endl;}void Sunshine(){ int &c = ans[0]; c = 0; TreeArray sub(s, arr); for ( int i =1, j =0; i <= n; i++) { while (j>0 && (pot.low[j+1] != cow.low[i]-sub.Low(cow.num[i]) || pot.equ[j+1] != cow.equ[i]-sub.Equ(cow.num[i]))) { for ( int k =i-j; k < i-nxt[j]; k++) sub.Add(cow.num[k], 1); j = nxt[j]; } if ( pot.low[j+1] == cow.low[i]-sub.Low(cow.num[i]) && pot.equ[j+1] == cow.equ[i]-sub.Equ(cow.num[i]) ) ++j; else sub.Add(cow.num[i], 1); if (j == k) ans[++c] = i-k+1; }}int main(){ //freopen("h:\\data.in", "r", stdin); //freopen("h:\\data.out", "w", stdout); scanf("%d%d%d", &n, &k, &s); for ( int i =1; i <= n; i++) scanf("%d", cow.num+i); for ( int i =1; i <= k; i++) scanf("%d", pot.num+i); Init(cow, n); Init(pot, k); GetNext(); Sunshine(); for ( int i =0; ans[0] && i<=ans[0]; i++) printf("%d\n", ans[i]); return 0;}
2:暴力数组实现
#include <iostream>#include <cstdio>#include <cstring>using namespace std;const int MAXN = 100000 + 1000;struct Cow{ int num[MAXN]; int low[MAXN][26]; // low[i][j]: i位置前比j小的元素数量 int equ[MAXN][26]; // equ[i][j]: i位置前(包括i)与j相等的元素数量};Cow cow, pot;int n, k, s;int nxt[MAXN];int ans[MAXN];void Init(Cow &cow, int n){ for ( int i =1; i <= n; i++) for ( int j =1; j <= s; j++) cow.low[i][j] = cow.low[i-1][j] + (cow.num[i]<j ? 1 : 0), cow.equ[i][j] = cow.equ[i-1][j] + (cow.num[i]==j ? 1 : 0);}void GetNext(){ nxt[1] = 0; for ( int i =2, j =0; i <= k; i++) { while (j>0 && (pot.low[j+1][pot.num[j+1]] != pot.low[i][pot.num[i]] - pot.low[i-j-1][pot.num[i]] || pot.equ[j+1][pot.num[j+1]] != pot.equ[i][pot.num[i]] - pot.equ[i-j-1][pot.num[i]])) j = nxt[j]; if (pot.low[j+1][pot.num[j+1]] == pot.low[i][pot.num[i]] - pot.low[i-j-1][pot.num[i]] && pot.equ[j+1][pot.num[j+1]] == pot.equ[i][pot.num[i]] - pot.equ[i-j-1][pot.num[i]]) ++j; nxt[i] = j; } //for ( int i =1; i <= k; i++) cout << nxt[i] << " "; cout << endl;}void Sunshine(){ int &c = ans[0]; c = 0; for ( int i =1, j =0; i <= n; i++) { while (j>0 && (pot.low[j+1][pot.num[j+1]] != cow.low[i][cow.num[i]] - cow.low[i-j-1][cow.num[i]] || pot.equ[j+1][pot.num[j+1]] != cow.equ[i][cow.num[i]] - cow.equ[i-j-1][cow.num[i]])) j = nxt[j]; if (pot.low[j+1][pot.num[j+1]] == cow.low[i][cow.num[i]] - cow.low[i-j-1][cow.num[i]] && pot.equ[j+1][pot.num[j+1]] == cow.equ[i][cow.num[i]] - cow.equ[i-j-1][cow.num[i]]) ++j; if (j == k) ans[++c] = i-k+1; }}int main(){ //freopen("h:\\data.in", "r", stdin); //freopen("h:\\data.out", "w", stdout); scanf("%d%d%d", &n, &k, &s); for ( int i =1; i <= n; i++) scanf("%d", &cow.num[i]); for ( int i =1; i <= k; i++) scanf("%d", &pot.num[i]); Init(cow, n); Init(pot, k); GetNext(); Sunshine(); for ( int i =0; ans[0] && i<=ans[0]; i++) printf("%d\n", ans[i]); return 0;}
第三种算法参照:http://www.cppblog.com/zxb/archive/2010/10/06/128782.aspx?opt=admin
//Time: 266MS#include <iostream>#include <cstdio>#include <cstring>using namespace std;const int MAXN = 100000 + 1000;int cow[MAXN], pot[MAXN];int occur[30], low[MAXN], high[MAXN];int nxt[MAXN];int ans[MAXN];int n, k, s;bool Comp(int *a, int *b, int k){ if (b[occur[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(){ nxt[1] = 0; for ( int i =2, j =0; i <= k; i++) { while (j>0 && !Comp(pot, pot+i-j-1, j+1)) j =nxt[j]; if (Comp(pot, pot+i-j-1, j+1)) ++j; nxt[i] = j; } //for ( int i =1; i <= k; i++) cout << nxt[i] << " "; cout << endl;}void Sunshine(){ int &c = ans[0]; c =0; for ( int i =1, j =0; i <= n; i++) { while ((j>0 && !Comp(pot, cow+i-j-1, j+1))) j =nxt[j]; if (Comp(pot, cow+i-j-1, j+1)) ++j; if (j == k) { ans[++c] = i-j+1; j = nxt[j]; } }}int main(){ //freopen("h:\\data.in", "r", stdin); //freopen("h:\\data.out", "w", stdout); scanf("%d%d%d", &n, &k, &s); for ( int i =1; i <= n; i++) scanf("%d", cow+i); for ( int i =1; i <= k; i++) scanf("%d", pot+i); for ( int i =1, j; i <= k; i++) { // occur, low, high, 值为0表示未出现。 if (occur[pot[i]] == 0) occur[pot[i]] = i; for ( j =pot[i]-1; j>0 && occur[j] == 0; --j); low[i] = occur[j]; for ( j =pot[i]+1; j<=s && occur[j] == 0; ++j); high[i] = occur[j]; } GetNext(); Sunshine(); for ( int i =0; ans[0] && i<=ans[0]; ++i) printf("%d\n", ans[i]); return 0;}
第四种算法是依次枚举模版串中第i大的数,然后分别进行kmp匹配。枚举时的第二大的数必须比第一大的大……
//Time: 954MS#include <iostream>#include <cstdio>#include <cstring>using namespace std;const int MAXN = 100000 + 1000;bool occur[30];int cow[MAXN], pot[MAXN];bool tcow[MAXN], tpot[MAXN];int match[MAXN], cnt[MAXN]; // 记录匹配次数int ans[MAXN];int nxt[MAXN];int n, k, s;void GetNext(){ nxt[1] = 0; for ( int i =2, j =0; i <= k; i++) { while (j>0 && tpot[j+1] != tpot[i]) j =nxt[j]; if (tpot[j+1] == tpot[i]) ++j; nxt[i] = j; }}void Sunshine(int c){ for ( int i =1; i <= n; i++) tcow[i] = (cow[i]==c ? true : false); for ( int i =1, j =0; i <= n; i++) { while (j>0 && tpot[j+1]!=tcow[i]) j =nxt[j]; if (tpot[j+1] == tcow[i]) ++j; if (j == k) { int p = i-j+1; if (cnt[p]==cnt[0] && match[p]<c) { cnt[p]++; match[p]=c; } j = nxt[j]; } }}int main(){ //freopen("h:\\data.in", "r", stdin); //freopen("h:\\data.out", "w", stdout); scanf("%d%d%d", &n, &k, &s); for ( int i =1; i <= n; i++) scanf("%d", cow+i); for ( int i =1; i <= k; i++) { scanf("%d", pot+i); occur[pot[i]] = true; } for ( int i =1; i <= s; i++) { // 枚举第i大的数是j if (!occur[i]) continue; for ( int j =1; j<= k; j++) tpot[j] = (pot[j]==i ? true : false); GetNext(); for ( int j =1; j <= s; j++) Sunshine(j); // 表示模版串中的i对应于原串中的j的时候的匹配结果 cnt[0]++; // 统计匹配次数 } for ( int i =1; i <= n; i++) if (cnt[i] == cnt[0]) ans[++ans[0]] = i; if (ans[0]) for ( int i =0; i <= ans[0]; ++i) printf("%d\n", ans[i]); return 0;}
ok.....这是一个kmp好题啊。
- poj 3167 Cow Patterns ... 四种算法 (均结合kmp)
- poj 3167 Cow Patterns kmp
- POJ 3167 Cow Patterns (KMP + 树状数组)
- poj 3167 Cow Patterns(kmp)
- POJ-3167- Cow Patterns(KMP)
- POJ 3167 Cow Patterns KMP+暴力
- poj 3167 Cow Patterns (kmp + 线段树/树状数组)
- [POJ] 3167 Cow Patterns (KMP+树状数组)
- POJ 3167 Cow Patterns
- POJ 3167 Cow Patterns
- POJ 3167 Cow Patterns
- POJ 3167 Cow Patterns
- POJ 3167 Cow Patterns
- pku 3167 Cow Patterns(kmp)
- POJ3167 Cow Patterns (KMP)
- [kmp] POJ3167 Cow Patterns
- Poj 3167 Cow Patterns Hdu 4749 Parade Show (KMP大小关系相同匹配+树状数组)
- Cow Patterns poj 3167 (hash解法)
- 0001
- 存储器管理小谈
- android文件浏览器
- 重复的数字
- 用css控制图片大小 让你不要再为图片变形烦恼
- poj 3167 Cow Patterns ... 四种算法 (均结合kmp)
- C++STL标准入门汇总
- 链表逆序
- 探索推荐引擎内部的秘密,第 1 部分: 推荐引擎初探
- 0002
- 迷茫
- static in C
- TCP/IP数据包解析示例
- 探索推荐引擎内部的秘密,第 2 部分: 深入推荐引擎相关算法 - 协同过滤