在网络赛期间罢赛不做题了过来写博客真是一种罪过,然而被零封实在是没有勇气坚持最后一个小时。。。。

两个队友都不在,单挑太水了


————————————————————————————————————————————————————————

进入正题

字符串常用算法


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神的模板保证比赛平安:

HDOJ 2222


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);
        }