首页 > 编程语言 > 详细

KMP算法题记

时间:2018-10-27 21:37:27      阅读:207      评论:0      收藏:0      [点我收藏+]

照着这篇博客刷一下。 自己也做一下笔记

poj 3461 Oulipo 

基于两个串a和b,问a在b中重复了几次。要对KMP进行一些修改,其实只是在模式串匹配完之后,ans++,并且让模式串的j回到原来的位置重来而已。

#include <cstdio>
#include <cstring>
using namespace std;
#define FOR(i,a,b) for(int i=(a);i<=(b);++i)
#define maxN 1000005
int nxt[maxN], n, m, cas, ans;
char a[maxN], b[maxN];
void getNxt(char *b, int m, int *nxt) {
  nxt[0] = -1;
  int i = 0, j = -1;
  while (i < m) {
    if (j == -1 || b[i] == b[j]) ++i, ++j, nxt[i] = j;
    else j = nxt[j];
  }
}
void kmp(char *a, int n, char *b, int m) {
  getNxt(b, m, nxt);
  int i = 0, j = 0;
  while (i < n) {
    while (-1 != j && a[i] != b[j]) j = nxt[j];
    ++i, ++j;
    if (j >= m) {
      ++ans;
      j = nxt[j];
    }
  }
}
int main () {
  // freopen("data.in", "r", stdin);
  scanf("%d", &cas);
  while (cas--) {
    scanf("%s%s", b, a);
    n = (int)strlen(a), m = (int)strlen(b);
    ans = 0;
    kmp(a, n, b, m);
    printf("%d\n", ans);
  }
  return 0;
}

 

poj 2752 Seek the Name, Seek the Fame

给你一个字符串,问前缀和后缀相同的字符串长度可以为多少?

考的是对next数组的理解。假设串为s,长度为L,那么next[L],即是s的最长前后缀长度,是答案之一,这里设这里的最长前缀为A,最长后缀为B。更短的”前缀-后缀串“必然也是A的前缀和B的后缀的公共部分,又因为A=B,那么问题变成了A的最长公共前后缀问题,如此便可不断回溯回去。

#include <cstdio>
#include <cstring>
using namespace std;
#define FOR(i,a,b) for(int i=(a);i<=(b);++i)
#define maxN 400005

char a[maxN];
int m, ans[maxN], nxt[maxN];
void getNxt(char *b, int m, int *nxt) {
  nxt[0] = -1;
  int i = 0, j = -1;
  while (i < m) {
    if (j == -1 || b[i] == b[j]) ++i, ++j, nxt[i] = j;
    else j = nxt[j];
  }
}
int main () {
  // freopen("data.in", "r", stdin);
  while (~scanf("%s", a)) {
    m = (int)strlen(a);
    getNxt(a, m, nxt);
    int cnt = 0;
    int cur = m, j = nxt[cur];
    while (j) ans[++cnt] = j, j = nxt[j];
    FOR(i, 1, cnt) printf("%d ", ans[cnt - i + 1]);
    printf("%d\n", m);
  }
  return 0;
}

 

poj 2406 Power Strings

问的是一个字符串,其最多能由多少个循环节构成。可以参考这篇说明

#include <cstdio>
#include <cstring>
using namespace std;
#define FOR(i,a,b) for(int i=(a);i<=(b);++i)
#define maxN 1000005
char s[maxN];
int nxt[maxN];
void getNxt(char *b, int m, int *nxt) {
  nxt[0] = -1;
  int i = 0, j = -1;
  while (i < m) {
    if (j == -1 || b[i] == b[j]) nxt[++i] = ++j;
    else j = nxt[j];
  }
}
int main () {
  // freopen("data.in", "r", stdin);
  while (~scanf("%s", s) && strcmp(s, ".")) {
    int m = (int)strlen(s);
    getNxt(s, m, nxt);
    if (m % (m - nxt[m]) == 0)
      printf("%d\n", m / (m - nxt[m]));
    else
      puts("1");
  }
  return 0;
}

 

hdu 3746 Cyclic Nacklace

问的是最少加入几个字符能使得这个串是循环的。

分几种情况:1,整个串无法被循环, 即nxt[m]=0,此时直接再来一个串接后面才行。

2,本身已经是循环串,此时m%(m-nxt[m])==0,直接输出0即可。

3,前缀是循环串,这个时候找到那个nxt[i]==0 (意味着前面就一个串,不循环),或者是i%(i-nxt[i])==0,此时即[0,i)这个串是循环串,得出最小循环节长度,设为L,答案就是L-后缀长度。

#include <cstdio>
#include <cstring>
using namespace std;
#define maxN 100006
int nxt[maxN], cas, idx;
char a[maxN];
void getNxt(char *v, int m) {
  nxt[0] = -1;
  int i = 0, j = -1;
  while (i < m) {
    if (j == -1 || v[i] == v[j]) nxt[++i] = ++j;
    else j = nxt[j];

    if (nxt[i] == 0 || i % (i - nxt[i]) == 0)
      idx = i;
  }
}

int main () {
  // freopen("data.in", "r", stdin);
  scanf("%d", &cas);
  while (cas--) {
    scanf("%s", a);
    int m = (int)strlen(a);
    getNxt(a, m);
    
    if (nxt[m] == 0) {
      printf("%d\n", m);
    } else if (m % (m - nxt[m]) == 0) {
      puts("0");
    } else {
      // 循环节长度
      int L = idx - nxt[idx];
      int tail = m - idx;
      printf("%d\n", L - tail);
    }
  }
  return 0;
}

 

hdu 3336 Count the string

问的是所有的前缀,在字符串中一共出现了几次?

假设某个前缀A和后缀B一样,那么B相当于给A贡献了B.length()分数。于是乎问题变成了:问有多少和前缀串相同的后缀串。但是因为如果单反前后缀一样就加分,会重复计算,比如说:

aaauvwaaa,第7和第8个a组成的aa会贡献2分,当加入第9个a成为aaa时,如果你又认为贡献3分,就会重复计算了第7个第8个连成的"aa"的分数。于是:当nxt[i]+1!=nxt[i+1]时,表示到i的后缀和到i+1的后缀是不一样的,才进行加分。

#include <cstdio>
using namespace std;
#define FOR(i,a,b) for(int i=(a);i<=(b);++i)
#define maxN 200005
int nxt[maxN], cas, n;
char a[maxN];
void getNxt(char *v, int m) {
  nxt[0] = -1;
  int i = 0, j = -1;
  while (i < m) {
    if (j == -1 || v[i] == v[j]) nxt[++i] = ++j;
    else j = nxt[j];
  }
}
int main () {
  // freopen("data.in", "r", stdin);
  scanf("%d", &cas);
  while (cas--) {
    scanf("%d %s", &n, a);
    getNxt(a, n);
    int ans = (n + nxt[n]) % 10007;
    FOR(i, 0, n - 1) {
      if (nxt[i] && nxt[i] + 1 != nxt[i + 1])
        ans = (ans + nxt[i]) % 10007;
    }
    printf("%d\n", ans);
  }
  return 0;
}

 

KMP算法题记

原文:https://www.cnblogs.com/Rosebud/p/9850122.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!