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

对于KMP算法,可以看我之前总结的这篇博客

hdu 3613 Best Reward

给一个字符串,字符由a~z构成,每个字符有一个权值。在某一点将字符串切成2半,若切成的字符串是回文的,则值为字符值之和,否则为0,问最大价值是多少?

设原串为S,S的逆记作T。则以T为主串,S为模式串做EKMP,若extend1[m-i]+m-i==m,则在i点切割,S[0~i)是回文的。若以S为主串,T为模式串做EKMP,若extend2[i]+i==m,则S[i,m)是回文的。具体为什么,可以看这个推理

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
#define FOR(i,a,b) for(int i=(a);i<=(b);++i)
#define maxN 500100
int v[], sum[maxN], nxt[maxN], extend1[maxN], extend2[maxN], cas;
char a[maxN], b[maxN]; void PRE_EKMP(char *x, int m, int *nxt) {
nxt[] = m;
int j = ;
while (j + < m && x[j] == x[j + ]) ++j;
nxt[] = j;
int k = ;
FOR(i, , m - ) {
int p = nxt[k] + k - ;
int L = nxt[i - k];
if (i + L - < p) nxt[i] = L;
else {
j = max(, p - i + );
while (i + j < m && x[i + j] == x[j]) ++j;
nxt[i] = j;
k = i;
}
}
}
void EKMP(char *x, int m, char *y, int n, int *nxt, int *extend) {
PRE_EKMP(x, m, nxt);
int j = ;
while (j < n && j < m && x[j] == y[j]) ++j;
extend[] = j;
int k = ;
FOR(i, , n - ) {
int p = extend[k] + k - ;
int L = nxt[i - k];
if (i + L < p + ) extend[i] = L;
else {
j = max(, p - i + );
while (i + j < n && j < m && y[i + j] == x[j]) ++j;
extend[i] = j;
k = i;
}
}
} int main () {
// freopen("data.in", "r", stdin);
scanf("%d", &cas);
while (cas--) {
sum[] = ;
FOR(i, , - ) scanf("%d", &v[i]);
scanf("%s", a);
int m = (int)strlen(a);
sum[] = v[a[] - 'a'];
FOR(i, , m - ) sum[i] = sum[i - ] + v[a[i] - 'a'];
FOR(i, , m - ) b[i] = a[m - i - ];
b[m] = ;

   // EKMP(a,b)意味着a为模式串,去匹配b
EKMP(a, m, b, m, nxt, extend1); // 前缀
EKMP(b, m, a, m, nxt, extend2); // 后缀
int ans = -1e8;
FOR(i, , m - ) {
int sc = , j = m - i;
if (extend1[j] + j == m) sc += sum[i - ];
if (extend2[i] + i == m) sc += sum[m - ] - sum[i - ];
ans = max(ans, sc);
}
printf("%d\n", ans);
}
return ;
}

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[] = -;
int i = , j = -;
while (i < m) {
if (j == - || 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 = , j = ;
while (i < n) {
while (- != 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 = ;
kmp(a, n, b, m);
printf("%d\n", ans);
}
return ;
}

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[] = -;
int i = , j = -;
while (i < m) {
if (j == - || 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 = ;
int cur = m, j = nxt[cur];
while (j) ans[++cnt] = j, j = nxt[j];
FOR(i, , cnt) printf("%d ", ans[cnt - i + ]);
printf("%d\n", m);
}
return ;
}

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[] = -;
int i = , j = -;
while (i < m) {
if (j == - || 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]) == )
printf("%d\n", m / (m - nxt[m]));
else
puts("");
}
return ;
}

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[] = -;
int i = , j = -;
while (i < m) {
if (j == - || v[i] == v[j]) nxt[++i] = ++j;
else j = nxt[j]; if (nxt[i] == || i % (i - nxt[i]) == )
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] == ) {
printf("%d\n", m);
} else if (m % (m - nxt[m]) == ) {
puts("");
} else {
// 循环节长度
int L = idx - nxt[idx];
int tail = m - idx;
printf("%d\n", L - tail);
}
}
return ;
}

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[] = -;
int i = , j = -;
while (i < m) {
if (j == - || 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]) % ;
FOR(i, , n - ) {
if (nxt[i] && nxt[i] + != nxt[i + ])
ans = (ans + nxt[i]) % ;
}
printf("%d\n", ans);
}
return ;
}

HDU 3374 String Problem  KMP+最大最小表示法:

问的是一个字符串,其最小和最大表示的起始下标,以及各自出现的次数。

至于次数,不论是最大还是最小,当然都是一样的,求一个循环次数即可,用KMP的next数组。

而最小表示法的思想是怎么样的呢?:

令i=0,j=1,k=0,表示从i开始k长度和j开始k长度的字符串相同。

那么若s[i]=s[j],此时k++,若s[i]>s[j],意味着i位置的字典序比j位置的字典序更大,需要移动,又因为i开始和j开始有k个字符相同,所以j可以待定保留,i需要变化,变化到未知的地方,即i+=k+1即可。

相反,若s[i]<s[j],同理j+=k+1。最后i和j中的较小者,是第一次出现最小表示的下标。

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

FZU 1901 Period II

For each prefix with length P of a given string S,ifS[i]=S[i+P] for i in [0..SIZE(S)-p-1],then the prefix is a “period” of S. We want to all the periodic prefixs.

需要输出满足period的p。 其实就是求所有的前后缀公共串。那么while(nxt[i]) 获取nxt[i],然后i=nxt[i];即可。

再而,那p和nxt[i]的关系又是什么呢?比如abcdxxxxabcd。对于最大的nxt[L]=4而言,即公共前缀为4的时候,i=L=12,第1个a要多少才到第2个a?距离刚好是L-nxt[L]

于是对于每个nxt[i],p就是L-nxt[i]。由于FZU挂了所以没交过,代码如下:

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

HDU 3613 待叙述, 比较值得品味

#include <cstdio>
#include <cstring>
#include <vector>
using namespace std; #define FOR(i,a,b) for(int i=(a);i<=(b);++i)
#define ll long long
#define maxN 2000005 // cnt代表前缀
struct node {int sum, son[], cnt;} nd[maxN];
struct str{
int st, ed;
str() {}
str(int s, int e) : st(s), ed(e) {}
};
vector<str> v; int fg[maxN][], nxt[maxN], tot, n, pre, l;
char s[maxN], t[maxN]; void getNxt(char *a, int m) {
nxt[] = -;
int i = , j = -;
while (i < m) {
if (j < || a[i] == a[j]) nxt[++i] = ++j;
else j = nxt[j];
}
} // f 0前缀 1后缀 a是模式串
void KMP(int f, char *a, char *b, int m, int start) {
int i = , j = ;
while (i < m) {
if (j < || a[j] == b[i]) ++i, ++j;
else j = nxt[j];
}
int pre = j;
if (f == ) {
while (pre) {
fg[start + pre - ][] = ;
pre = nxt[pre];
}
} else {
while (pre) {
fg[start + l - pre][] = ;
pre = nxt[pre];
}
}
} void insert(int pre, char *a, int start, int L) {
FOR(i, , l - ) {
int cur = a[i] - 'a';
if (!nd[pre].son[cur]) {
++tot;
nd[pre].son[cur] = tot;
pre = tot;
} else pre = nd[pre].son[cur];
if (i + < L) nd[pre].cnt += fg[start + i + ][];
}
nd[pre].sum++;
} ll query(int start, int en, int pre) {
ll sym = , ans = , l = en - start;
FOR(i, start, en - ) {
int cur = t[i] - 'a';
if (nd[pre].son[cur]) {
pre = nd[pre].son[cur];
if (fg[start + l - (i - start + ) - ][] || i == en - )
ans += nd[pre].sum;
} else {
sym = ;
break;
}
}
if (sym) ans += nd[pre].cnt;
return ans;
} int main() {
// freopen("data.in", "r", stdin);
pre = ;
scanf("%d", &n);
ll ans = ;
while (n--) {
scanf("%d %s", &l, s + pre);
v.push_back(str(pre, pre + l));
FOR(i, pre, pre + l - ) t[i] = s[pre + l - (i - pre + )];
getNxt(s + pre, l);
KMP(, s + pre, t + pre, l, pre);
getNxt(t + pre, l);
KMP(, t + pre, s + pre, l, pre);
insert(, s + pre, pre, l);
pre += l;
}
FOR(i, , (int)v.size() - )
ans += query(v[i].st, v[i].ed, );
printf("%lld\n", ans);
}

hdu 4513 吉哥系列故事--完美队形II

问最长回文串,但要求左到中是非递减的,对称的,就是中到右是非递增的。

#include <cstdio>
#include <algorithm>
using namespace std;
#define FOR(i,a,b) for(int i=(a);i<=(b);++i)
#define maxN 1000005
int cas, n, T[maxN*];
int s1[maxN], s2[maxN*]; void manacher() {
s2[] = -, s2[] = -;
int j = ;
FOR(i, , n - )
s2[j++] = s1[i], s2[j++] = -; int id = , mx = ;
T[] = ;
FOR(i, , * n + ) {
T[i] = (i < mx) ? min(T[ * id - i], mx - i) : ;
while (s2[i - T[i]] == s2[i + T[i]]) {
if (s2[i + T[i]] != -) {
if (s2[i + T[i]] <= s2[i + T[i] - ]) T[i]++;
else break;
}
T[i]++;
}
if (i + T[i] > mx)
id = i, mx = i + T[i];
}
} int main () {
// freopen("data.in", "r", stdin);
scanf("%d", &cas);
while (cas--) {
scanf("%d", &n);
FOR(i, , n - )
scanf("%d", &s1[i]);
manacher();
int ans = ;
FOR(i, , * n + ) ans = max(ans, T[i]);
printf("%d\n", ans - );
}
return ;
}

51nod 1554 欧姆诺姆和项链

给数字n和k,n和k都∈[1,100000],再给一个字符串a,问前几个字符可构成这样的形式:ABAB...ABA,其中A有k+1个,B有k个,A和B可为空。前i个可以的时候输出1,否则0.

只有两种可能:SSSS..SS,或者是SSSS..ST。

假设为前者,那么循环节长度为i/(i-nxt[i]),设为x,又因为有k对AB,所以AB包含了x/k对S,剩下x%k个S就是A包含的S的个数,因B可以为空,所以x/k>=x%k即可。

假设为后者,其中T是S的一个前缀,即A,与前者一样,循环节长度为i/(i-nxt[i]),设为x,又因有k对AB,那么AB包含了x/k对S,剩下x%k个S ,以及单独的T,所以只要x/k<x%k即可。

#include <stdio.h>
#define FOR(i,a,b) for(int i=(a);i<=(b);++i)
#define maxN 1000009
int f[maxN];
char a[maxN]; void getf(char *x, int m) {
f[] = -;
int i = , j = -;
while (i < m) {
if (j < || x[i] == x[j]) f[++i] = ++j;
else j = f[j];
}
} int main () {
// freopen("data.in", "r", stdin);
int n, k;
scanf("%d%d%s", &n, &k, a);
getf(a, n);
FOR(i, , n) {
int x = i / (i - f[i]);
if (i % (i - f[i])) {
if (x / k > x % k) printf("");
else printf("");
} else {
if (x / k >= x % k) printf("");
else printf("");
}
}
return ;
}

51nod 1277

给一个字符串,前缀有2个属性,长度和在字符串中出现的次数。

问:所有前缀中,长度*字符串出现的次数 最大值是多少?

g[i]记录长度为i的前缀出现的次数。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define FOR(i,a,b) for(int i=(a);i<=(b);++i)
#define ll long long
#define maxN 100005
char a[maxN];
int f[maxN], g[maxN]; void init(int m) {
f[] = -;
int i = , j = -;
while (i < m) {
if (j < || a[i] == a[j]) f[++i] = ++j;
else j = f[j];
}
} int main () {
// freopen("data.in", "r", stdin);
scanf("%s", a);
int m = (int)strlen(a);
init(m);
for (int i = m; i >= ; --i)
g[i]++, g[f[i]] += g[i];
ll ans = ;
for (ll i = ; i <= m; ++i)
ans = max(i * g[i], ans);
printf("%lld\n", ans);
return ;
}

KMP、扩展KMP、Manacher习题的更多相关文章

  1. 字符串匹配—KMP 扩展KMP Manacher

    kuangbin字符串专题传送门--http://acm.hust.edu.cn/vjudge/contest/view.action?cid=70325#overview 算法模板: KMP: ; ...

  2. Kuangbin 带你飞 KMP扩展KMP Manacher

    首先是几份模版 KMP void kmp_pre(char x[],int m,int fail[]) { int i,j; j = fail[] = -; i = ; while (i < m ...

  3. ACM之路(12)—— KMP & 扩展KMP & Manacher

    最近做完了kuangbin的一套关于kmp的题目(除了一道字典树的不会,因为还没学字典树所以先放放),做个总结.(kuangbin题目的链接:http://acm.hust.edu.cn/vjudge ...

  4. 【string】KMP, 扩展KMP,trie,SA,ACAM,SAM,最小表示法

    [KMP] 学习KMP,我们先要知道KMP是干什么的. KMP?KMPLAYER?看**? 正如AC自动机,KMP为什么要叫KMP是因为它是由三个人共同研究得到的- .- 啊跑题了. KMP就是给出一 ...

  5. kmp&扩展kmp

    kmp: KMP的主要目的是求B是不是A的子串,以及若是,B在A中所有出现的位置 写的很详细的大佬的博客:http://www.matrix67.com/blog/archives/115 模板: / ...

  6. kuangbin专题十六 KMP&&扩展KMP HDU2609 How many (最小字符串表示法)

    Give you n ( n < 10000) necklaces ,the length of necklace will not large than 100,tell me How man ...

  7. hdu 4300 Clairewd’s message(kmp/扩展kmp)

    题意:真难懂.. 给出26个英文字母的加密表,明文中的'a'会转为加密表中的第一个字母,'b'转为第二个,...依次类推. 然后第二行是一个字符串(str1),形式是密文+明文,其中密文一定完整,而明 ...

  8. [kuangbin带你飞]专题十六 KMP & 扩展KMP & Manacher 题解报告

    来刷kuangbin字符串了,字符串处理在ACM中是很重要的,一般比赛都会都1——2道有关字符串处理的题目,而且不会很难的那种,大多数时候都是用到一些KMP的性质或者找规律. 点击标题可跳转至VJ比赛 ...

  9. kuangbin专题十六 KMP&&扩展KMP HDU3613 Best Reward(前缀和+manacher or ekmp)

    After an uphill battle, General Li won a great victory. Now the head of state decide to reward him w ...

  10. kuangbin专题十六 KMP&&扩展KMP HDU2328 Corporate Identity

    Beside other services, ACM helps companies to clearly state their “corporate identity”, which includ ...

随机推荐

  1. Centos7.X通过rpm包安装Docker

    目录 前言 1.Docker官网下载rpm包 2.通过liunx命令安装rpm包 3.迁移镜像存储路径 前言 Docker已经火了很多年,现在各大公司都会使用它.那么在我们日常开发中也经常使用,比如我 ...

  2. 用MVC5+EF6+WebApi 做一个小功能(二) 项目需求整理

    在一个项目开始前,需求整理大概要占到整个项目周期15%甚至30%的比重,可以说需求理得越清楚,后续开发中返工几率越小.在一个项目中,开发新功能的花费的精力要远远小于修改功能的精力,这基本是一个共识.老 ...

  3. Visual Studio 2017 插件扩展

    “工具善其事,必先利其器!装好这些插件让vs更上一层楼” ReSharper : 首先的是Resharper,这个基本是目前是我开发过程中必备的工具集,唯一的缺点就是吃内存,所以你的内存要是低于8G, ...

  4. 批处理TOMCAT8.0自动重启任务

    @echo title tomcat重启 set num=7001  //端口号,根据tomcat的设置项设置set JAVA_HOME=D:\software\Java\jdk1.8.0_131 / ...

  5. java工具包一:日期处理

    作者:NiceCui 本文谢绝转载,如需转载需征得作者本人同意,谢谢. 本文链接:http://www.cnblogs.com/NiceCui/p/7846812.html 邮箱:moyi@moyib ...

  6. java.lang.ExceptionInInitializerError异常

    今天在开发的过程中,遇到java.lang.ExceptionInInitializerError异常,百度查了一下,顺便学习学习,做个笔记 静态初始化程序中发生意外异常的信号,抛出Exception ...

  7. 初识Nosql

    ref:http://www.runoob.com/mongodb/nosql.html  https://blog.csdn.net/testcs_dn/article/details/512258 ...

  8. js-JavaScript实现数字的千位分隔符

    function thousandSeparator(num) { return num && (num .toString().indexOf('.') != -1 ? num.to ...

  9. 禅道项目管理软件 为提交Bug页面设置bug必填字段

    为提交Bug页面设置bug必填字段 by:授客 QQ:1033553122 测试环境: 禅道项目管理软件7.1.stable版本 注:仅适合windows版 步骤1.找到xampp\zentao\mo ...

  10. Expo大作战(二十四)--expo sdk api之Accelerometer

    简要:本系列文章讲会对expo进行全面的介绍,本人从2017年6月份接触expo以来,对expo的研究断断续续,一路走来将近10个月,废话不多说,接下来你看到内容,讲全部来与官网 我猜去全部机翻+个人 ...