poj KMP例题

poj-2406 kmp循环节

http://poj.org/problem?id=2406

KMP,next表示模式串如果第i位(设str[0]为第0位)与文本串第j位不匹配则要回到第next[i]位继续与文本串第j位匹配。则模式串第1位到next[n]与模式串第n-next[n]位到n位是匹配的。所以思路和上面一样,如果n%(n-next[n])==0,则存在重复连续子串,长度为n-next[n]。

例如:a    b    a    b    a    b

next:-1   0    0    1    2    3    4

next[n]==4,代表着,前缀abab与后缀abab相等的最长长度,这说明,ab这两个字母为一个循环节,长度=n-next[n]
code:

#include
#include
#include
#include
using namespace std;
#define N 1000005
int nexts[N];
void get_m(char *s) {
	int len = strlen(s);
	int j = -1, i = 0;
	nexts[0] = -1;
	while (i < len) {
		while (-1 != j && s[j] != s[i]) {
			j = nexts[j];
		 }
		nexts[++i] = ++j;
	}
}
int main() {
	char s[N];
	while (~scanf("%s", s) && s[0] != '.') {
		//scanf("%s", s);
		get_m(s);
		int len = strlen(s);
		if (len % (len - nexts[len]) == 0) {
			cout << len / (len - nexts[len]) << endl;
		}
		else {
			cout << "1" << endl;
		}
	}
	return 0;
}

poj-3461

http://poj.org/problem?id=3461

最简单的KMP题,找出第一个字符串在第二个字符串中出现次数.

code:

#include 
#include 
#include 
#define Memset(x, a) memset(x, a, sizeof(x))
using  namespace std;
const int N=1e6+10;
char  w[N],t[N];
int next[N];
int sum;
 
void  getNext(const char P[],int next[]){
  int  m=strlen(P);
  int i=0,j;
  j=next[0]=-1;
  while(i=m){
      sum++;
      j=next[j];//这儿修改很重要,不然会超时
    }
  }
}
 
int main(){
  int T;
  scanf("%d",&T);
  while(T--){
    sum=0;
    Memset(next,0);
    scanf("%s%s",w,t);
    kmp(t,w,next);
    printf("%d\n",sum);
  }
  return 0;
}

poj-2752

题意给出一个字符串,求串中可能出现的前后缀,从小到大输出。

那么算法描述就是: 
第一步:求next[len2], 即next[18] = 9; 
第二步:把9代进来,即求next[9] = 4; 
第三步:把4代进来,即求next[4] = 2; 
第四步:next[2] = 0; 也就是下标2之前的串已经没有前后缀可以匹配了 
所以答案就是: 2 4 9 18 【PS: 从小到大输出,18是串长,显然符合题意】 

code:

#include
#include
#include
#include
#include
using namespace std;
#define N 400005
int nexts[N];
void get_m(string str) {
	int i = 0;
	int j = -1;
	nexts[0] = -1;
	while (i < str.size()) {
		while (j != -1 && str[i] != str[j]) {
			j = nexts[j];
		}
		nexts[++i] = ++j;
	}
}
int main() {
	int a[N], cnt = 0;
	string str;
	while (cin >> str) {
		cnt = 0;
		get_m(str);
		int temp = nexts[str.size()];
		while (temp != 0) {
			a[cnt++] = temp;
			temp = nexts[temp];
		}
		for (int i = cnt - 1; i >= 0; i--) 
				cout << a[i] << " ";
		cout << str.size() << endl;
		cin.get();
	}
	return 0;
}

poj-1961

题意:

给你一个字符串,求这个字符串到第i个字符为止的循环节的次数。

比如aabaabaabaab,长度为12.到第二个a时,a出现2次,输出2.到第二个b时,aab出现了2次,输出2.到第三个b时,aab出现3次,输出3.到第四个b时,aab出现4次,输出4.

思路:

这道题就是2406的加强版而已。2406输出一个字符串的循环节出现的次数,这个是到第i个字符为止,其实就是多了一层循环。把这个字符串遍历一次即可,具体思路也以参考小白书的例题。

code:

#include 
#include 
#include 
#define Memset(x, a) memset(x, a, sizeof(x))
using  namespace std;
const int N=1e6+10;
char s[N];
int next[N];
int n;
 
void  getNext(const char P[],int next[]){
  int  m=strlen(P);
  int i=0,j;
  j=next[0]=-1;
  while(i0&&i%(i-next[i])==0)printf("%d %d\n",i,i/(i-next[i]));
    }
    printf("\n");
  }
  return 0;
}

 

你可能感兴趣的:(算法)