1. 程式人生 > >hdu 4333 擴展kmp+kmp重復字串去重

hdu 4333 擴展kmp+kmp重復字串去重

cnblogs .html 擴展kmp 兩個 代碼 ++i not pri mod

題目鏈接:http://acm.hdu.edu.cn/showproblem.php?pid=4333

關於kmp next數組求最短重復字串問題請看;http://www.cnblogs.com/z1141000271/p/7406198.html

擴展kmp請看:http://www.cnblogs.com/z1141000271/p/7404717.html

題目大意:一個數字,依次將第一位放到最後一位,問小於本身的數的個數及等於本身的個數和大於本身的個數,但是要註意重復的不再計算

題解:我們把得到的串double一下(比如s=aa double之後 s=aaaa),然後對double之後得到的串做一個次擴展kmp,然後便利得到的Next數組。如果公共前綴匹配>=原串長度則e++,對於公共前綴小於原串長度的,我們去比較失配的第一位就可以了。再就是去重的問題,這個要註意!如果原串可以由更小的串重復得到,那我們計算的結果就有很多重復的。

(註意double 以及去重兩個操作的使用)

ac代碼:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#define mt(a) memset(a,0,sizeof(a))
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
int extend[200001];
int Next[200001];
int min(int x,int
y) { if(x>y) return y; return x; } int knext[200005]; void getknext(char s[],int len) { mt(knext); int i=0; int j=-1; knext[i]=j; while(i<len) { if(j==-1 || s[i]==s[j]) knext[++i]=++j; else j=knext[j]; } } void getNext(char t[],int len) { mt(Next);
//ll len=strlen(t); Next[0]=len; int a,p; a=1; while( a<len && t[a]==t[a-1]) a++; // 求出長度為1的時候 解為多少 Next[1]=a-1; a=1; for(int i=2;i<len;i++) // 後續的按照算法來就好 { p=a+Next[a]-1; if((i-1)+Next[i-a] < p ) Next[i]=Next[i-a];// 第一種情況 沒有超過等於的部分 else // 超過的話就不好直接用next的定義 需要後續的遍歷 { ll j = (p - i + 1) > 0 ? (p - i + 1) : 0; while(i + j < len && t[i+j] == t[j]) j++; Next[i]=j; a=i; } } } char s[2000005];// s->exkmp t->Next int main() { int Case; scanf("%d",&Case); for(int z=1;z<=Case;z++) { scanf("%s",s); int len=strlen(s); int j=0; int l,e,g; getknext(s,len); int temp=len-knext[len]; // 最小重復長度 if(len%temp) temp=1; else temp=len/temp; for(int i=0;i<len;i++) { s[len+i]=s[i]; } getNext(s,2*len); l=e=g=0; for(int i=0;i<len;i++) { if(Next[i]>=len) e++; else { int temp=i+Next[i];// postion of not match if(s[temp]<s[Next[i]]) l++; else if(s[temp]>s[Next[i]])g++; } } printf("Case %d: %d %d %d\n",z,l/temp,e/temp,g/temp); } return 0; }

hdu 4333 擴展kmp+kmp重復字串去重