在网络赛期间罢赛不做题了过来写博客真是一种罪过,然而被零封实在是没有勇气坚持最后一个小时。。。。
两个队友都不在,单挑太水了
————————————————————————————————————————————————————————
进入正题
字符串常用算法
Part 1:字符Hash
模版上述地方有,根据题目对自己的理解进行解释:
HDOJ 4821
题意:给定m和l和一个字符串,
求该串中连续m个长为l的子串互不重复的数目
char s[maxn];
ull base[maxn],Hash[maxn];
map<ull,int> mp;
int main(){
//input;
int m,l,i,len,ans;
base[0]=1;
for(i=1;i<maxn;i++) base[i]=base[i-1]*seed;
while(scanf("%d%d",&m,&l)!=EOF){
scanf("%s",s);
ans=0;
len=strlen(s);
Hash[len]=0;
for(i=len-1;i>=0;i--)
Hash[i]=Hash[i+1]*seed+s[i]-'a';
for(i=0;i<l&&i+m*l<len;i++){//枚举以i位置为起点,长度为m*l的子串是否合法
mp.clear();
for(int j=i;j<i+m*l;j+=l){//
ull temp=Hash[j]-Hash[j+l]*base[l];
mp[temp]++;
}
if (mp.size()==m) ans++;
for(int j=i+m*l;j+l<=len;j+=l){//更换起点,i+km为起点依次往后推
ull temp=Hash[j-m*l]-Hash[j-(m-1)*l]*base[l];
mp[temp]--;
if (!mp[temp]) mp.erase(temp);
temp=Hash[j]-Hash[j+l]*base[l];
mp[temp]++;
if (mp.size()==m) ans++;
}
}
printf("%d\n",ans);
}
return 0;
}
HDOJ 4080
/*
字符串题目注意:
Hash,Rank这类单词一定首字母大写,不然CE
题意:给定一个字符串和出现次数k,
找出至少出现k次的最长的子串长度以及其最后一次出现的位置(首字符的位置)
二分判断某个长度是否可行,找到最大的。。。。
按照哈希值的大小排序:越大的位数“相对” 越长
*/
int l,len,pos,Rank[maxn];
ull Hash[maxn],xp[maxn],haha[maxn];
char str[maxn];
bool cmp(int a,int b){
/*if (haha[a]==haha[b]) return a<b;
return haha[a]<haha[b];*/
return (haha[a]!=haha[b])?(haha[a]<haha[b]):(a<b);
}
bool solve(int mid){
int c=1;
pos=-1;
for(int i=0;i<len-mid+1;i++){
Rank[i]=i;
haha[i]=Hash[i]-Hash[i+mid]*xp[mid];
}
sort(Rank,Rank+len-mid+1,cmp);
//注意第二个参数,len-mid+1描述的是数组中元素个数
//开始找
for(int i=0;i<len-mid+1;i++){
if (i==0||haha[Rank[i]]!=haha[Rank[i-1]]) c=1;
else{
c++;
if (c>=l) pos=max(pos,Rank[i]);
}
}
return pos>=0;
}
int main(){
//input;
while(scanf("%d",&l)!=EOF){
if (!l) break;
scanf("%s",str);
len=strlen(str);
if (l==1){//特判,串长为1
printf("%d %d\n",len,0);
continue;
}
memset(Hash,0,sizeof(Hash));
memset(xp,0,sizeof(xp));
xp[0]=1;
for(int i=len-1;i>=0;i--)
Hash[i]=Hash[i+1]*x+str[i]-'a';
for(int i=1;i<len;i++) xp[i]=xp[i-1]*x;
if (!solve(1)){//又是细节判断,无解输出
puts("none");
continue;
}
int low=1,high=len,mid;
while(low+1<high){
mid=(low+high)>>1;
if (solve(mid)) low=mid;
else high=mid;
}
solve(low);
printf("%d %d\n",low,pos);
}
return 0;
}
Trie(字典树)原理
POJ3630
POJ3630题解
KMP算法
我bin带你飞的专题直接刷爆把:练习KMP
AC自动机(听妈妈说学过这个算法就一定AK)
看过算法之后,学学我bin神的模板保证比赛平安:
struct Trie{
int next[500010][26],fail[500010],end[500010];
int root,L;
int newnode(){
for(int i=0;i<26;i++) next[L][i]=-1;
end[L++]=0;
return L-1;
}
void init(){
L=0;
root=newnode();
}
void insert(char buf[]){
int len=strlen(buf);
int now=root;
for(int i=0;i<len;i++){
if (next[now][buf[i]-'a']==-1) next[now][buf[i]-'a']=newnode();
now=next[now][buf[i]-'a'];
}
end[now]++;
}
void build(){
queue<int> Q;
fail[root]=root;
for(int i=0;i<26;i++)
if (next[root][i]==-1) next[root][i]=root;
else{
fail[next[root][i]]=root;
Q.push(next[root][i]);
}
while(!Q.empty()){
int now=Q.front();
Q.pop();
for(int i=0;i<26;i++)
if (next[now][i]==-1)
next[now][i]=next[fail[now]][i];
else{
fail[next[now][i]]=next[fail[now]][i];
Q.push(next[now][i]);
}
}
}
int query(char buf[]){
int len=strlen(buf);
int now=root;
int res=0;
for(int i=0;i<len;i++){
now=next[now][buf[i]-'a'];
int temp=now;
while(temp!=root){
res+=end[temp];
end[temp]=0;
temp=fail[temp];
}
}
return res;
}
};
char buf[1000010];
Trie ac;
int main(){
//input;
int T,n;
scanf("%d",&T);
while(T--){
scanf("%d",&n);
ac.init();
for(int i=0;i<n;i++){
scanf("%s",buf);
ac.insert(buf);
}
ac.build();
scanf("%s",buf);
printf("%d\n",ac.query(buf));
}
return 0;
}
习题链接:我bin带你飞——AC自动机专题
第一题HDOJ2222模板题
第二题HDOJ2896需要改动模板:对end数组中记录各个字符串的编号
第三题HDOJ3065需要改动模板:每个字符串在模式串之中匹配了几次(在query中添加一个标记数组)
第四题ZOJ3430需要添加一个base64的解码算法。。。弱写了太久,还是WA,直接搬运我bin的吧:
unsigned char buf[2050];
int tot;
char str[4000];
unsigned char s[4000];
unsigned char Get(char ch)
{
if( ch>='A'&&ch<='Z' )return ch-'A';
if( ch>='a'&&ch<='z' )return ch-'a'+26;
if( ch>='0'&&ch<='9' )return ch-'0'+52;
if( ch=='+' )return 62;
else return 63;
}
void change(unsigned char str[],int len)
{
int t=0;
for(int i=0;i<len;i+=4)
{
buf[t++]=((str[i]<<2)|(str[i+1]>>4));
if(i+2 < len)
buf[t++]=( (str[i+1]<<4)|(str[i+2]>>2) );
if(i+3 < len)
buf[t++]= ( (str[i+2]<<6)|str[i+3] );
}
tot=t;
}
main():
int n,m;
while(scanf("%d",&n) == 1)
{
ac.init();
for(int i = 0;i < n;i++)
{
scanf("%s",str);
int len = strlen(str);
while(str[len-1]=='=')len--;
for(int j = 0;j < len;j++)
{
s[j] = Get(str[j]);
}
change(s,len);
ac.insert(buf,tot,i);
}