字符串问题模板 长期更新


后缀数组


#include  
#include  
#include  
#include  
using namespace std;  
const int maxn = 1e5+5;  
int t1[maxn], t2[maxn], c[maxn]; //c 基数排序辅助数组
// 
int ra[maxn], height[maxn]; //rank数组,高度数组 
int sa[maxn];  //suffix array  
char str[maxn];  
int n;  
  
bool cmp(int *r, int a, int b, int l)  
{  
    return r[a]==r[b]&&r[a+l]==r[b+l];  
}  
  
void da(char str[], int sa[], int ra[], int height[], int n, int m)  
{  
    n++;  
    int i, j, p, *x = t1, *y = t2;  
    for(i = 0; i < m; i++) c[i] = 0;  
    for(i = 0; i < n; i++) c[x[i]=str[i]]++;  
    for(i = 1; i < m; i++) c[i] += c[i-1];  
    for(i = n-1; i >= 0; i--) sa[--c[x[i]]] = i;  
    for(j = 1; j <= n; j<<=1)  
    {  
        p = 0;  
        for(i = n-j; i < n; i++) y[p++] = i;  
        for(i = 0; i < n; i++) if(sa[i] >= j) y[p++] = sa[i]-j;  
        for(i = 0; i < m; i++) c[i] = 0;  
        for(i = 0; i < n; i++) c[x[y[i]]]++;  
        for(i = 1; i < m; i++) c[i] += c[i-1];  
        for(i = n-1; i >= 0; i--) sa[--c[x[y[i]]]] = y[i];  
        swap(x, y);  
        p = 1; x[sa[0]] = 0;  
        for(i = 1; i < n; i++)  
            x[sa[i]] = cmp(y, sa[i-1], sa[i], j) ? p-1 : p++;  
        if(p >= n) break;  
        m = p;  
    }  
    int k = 0;  
    n--;  
    for(i = 0; i <= n; i++) ra[sa[i]] = i;  
    for(i = 0; i < n; i++)  
    {  
        if(k) k--;  
        j = sa[ra[i]-1];  
        while(str[i+k]==str[j+k]) k++;  
        height[ra[i]] = k;  
    }  
}  
  
int main(void)  
{  
    while(~scanf(" %s", str))  
    {  
        n = strlen(str);  
        da(str, sa, ra, height, n, maxn);  
        for(int i = 1; i <= n; i++)  
            printf("%d%c", sa[i]+1, i==n ? '\n' : ' ');  
        for(int i = 2; i <= n; i++)  
            printf("%d%c", height[i], i==n ? '\n' : ' ');  
    }  
    return 0;  
}  



字典树

(基本操作就是插入和查询,求以某一个字符串为前缀的单词的数量)



指针(动态模板)

#include
#include
#include
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int maxn=1e5+10;
struct Trie{
	Trie *next[26];//next是表示每层有多少种类的数,如果只是小写字母,则26即可,	
				  //	若改为大小写字母,则是52,若再加上数字,则是62了,这里根据题意来确定。 
	int num;
//num可以表示一个字典树到此有多少相同前缀的数目,这里根据需要应当学会自由变化。

	Trie()
	{
		int i;
		for(int i=0;i<26;i++)
		next[i]=NULL;
		num=0;
	 } 
	
}root;
void insert(char s[])//将字符串s插入到字典树中
{
	Trie *p=&root;
	int i;
	for(int i=0;s[i];i++)//遍历s的每一个字符 
	{
		if(p->next[s[i]-'a']==NULL) //如果没有该字符对应的节点 
		p->next[s[i]-'a']=new Trie;
		p=p->next[s[i]-'a'];
		p->num++; 
	}
 } 
int find(char s[])//返回以字符串s为前缀的单词的数量
{
	Trie *p=&root;
	int i;
	for(int i=0;s[i];i++)//在字典树找到该单词的结尾位置
	{
		if(p->next[s[i]-'a']==NULL)
		return 0;
		p=p->next[s[i]-'a']; 
	}
	return p->num;
}
int main(){
	
	char s[20];
	while(gets(s))
	{
		if(strlen(s)==0||s[0]==NULL)
		break;
		insert(s);
	}
	while(~scanf("%s",s))
	{
		printf("%d\n",find(s));
	}
	return 0;
}




静态模板


#include
#include
#include
#include 
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int maxn=1e6+10;
int trie[maxn][26];
//数组形式定义字典树,值存储的是下一个字符的位置
int num[maxn]={0};
//附加值,以某一字符串为前缀的单词的数量 
int pos=1; 
void insert(char s[])//在字典树中插入单词s
{
	int c=0;
	for(int i=0;s[i];i++)
	{
		int n=s[i]-'a';
		if(trie[c][n]==0)//如果对应字符还没有值
			trie[c][n]=pos++;
		c=trie[c][n];
		num[c]++; 
	 } 
} 
int find(char s[])//返回以某个字符串为前缀的单词的数量
{
	int c=0;
	for(int i=0;s[i];i++)
	{
		int n=s[i]-'a';
		if(trie[c][n]==0)
		return 0;
		c=trie[c][n];
	 } 
	 return num[c];
} 
int main(){

	char s[20];
	while(gets(s))
	{
		if(strlen(s)==0||s[0]==NULL)
		break;
		insert(s);
	}
	while(~scanf("%s",s))
	{
		printf("%d\n",find(s));
	}
	return 0;
}






01trie

#include  
#include  
#include  
#include   
using namespace std;  
typedef long long ll;  
const int mod=1e9+7;  
const int maxn=6e6+10;
const int N=3e5+10;  
int trie[maxn][5];
int num[maxn];  
int vis[2*N];
int pos=1;   
int now;
char str[25];
void insert(char s[],int val)  
{  
    int c=0;  
    int len = strlen(s);
    for(int i=0;i < len;i++)  
    {  
        int bt=s[i]-'0';  
        if(trie[c][bt]==0) 
            {
              num[pos] = 0;
              memset(trie[pos],0,sizeof trie[pos]);
			  trie[c][bt]=pos++;
			}
        c=trie[c][bt]; 
     }   
	 num[c] = val;   
	 return ;
}   
int find(char s[])  
{  
    int c=0;  
    int len = strlen(s);
    for(int i=0;i < len;i++)  
    {  
        int bt=s[i]-'0';  
        if(trie[c][bt])  
        c=trie[c][bt];
        else
        c=trie[c][1-bt];
     }   
     return num[c];  
}   
void change(int i)
{
				str[21]='\0';
				int tmp = i;
				for(int j = 20;j >= 0;j--)
				{
					if(tmp)
					{
						str[j] = tmp%2 +'0';
						tmp /= 2; 
					}
					else
						str[j] = '0';
				}
}
int main(){  
  
  		int n,m;
  		cin>>n>>m; 
    	for(int i = 1;i <= n;i++)
		{
			int a;
			scanf("%d",&a);
			if(vis[a]==0)
			vis[a] = 1;				
		}	
		for(int i = 0;i <= 600000 ;i++)
		{
			if(vis[i] == 0)
			{
				change(i);
				insert(str,i);
			}
		}
		now = 0;
		while(m--)
		{
			int x;
			scanf("%d",&x);
			now ^= x;
			x = now;
			change(x);
			printf("%d\n",find(str)^now);
		}
    return 0;  
}  


字符串哈希


#include
using namespace std;

typedef unsigned long long ull;
const ull base=123;
const int maxn=4e5+10;
const int hashh= 1e6+10;

char s[511][511];
ull num[511*511],f[511][511];
int n,m; 
struct hashmap
{
	ull a[maxn];
	int head[hashh];
	int nxt[maxn];
	int siz;
	void init()
	{
		memset(head,-1,sizeof head);
		siz = 0;
	}
	bool find(ull val)
	{
		int tmp = (val % hashh + hashh) % hashh;
		for(int i = head[tmp];i!=-1;i = nxt[i])
		{
			if(val == a[i]) return true;
		}
		return false;
	}
	void add(ull val)
	{
		int tmp = (val % hashh + hashh) % hashh;
		if(find(val)) return ;
		a[siz] = val;
		nxt[siz] = head[tmp];//令next指向-1;
		head[tmp] = siz++; 
	}
}Hash;

void init()
{
	memset(f,0,sizeof f);
	num[0]=1;
	int cnt = 0;
	for(int i = 1; i <= n;i++)
	 for(int j = 1;j <= m;j++)
	 {
	 	cnt++;
	 	num[cnt] = num[cnt-1]*base;
	 	f[i][j] = f[i-1][j] + f[i][j-1] -f[i-1][j-1] + (s[i][j]-'0')*num[cnt];
	 }
	 return ;
}
bool check(int x)
{
	Hash.init();
	for(int i = x;i <= n;i++)
	 for(int j = x;j <= m;j++)
	 {
	 	ull tmp = f[i][j] - f[i][j - x] - f[i - x][j] + f[i - x][j - x];
	 	tmp = tmp * num[(n - i) * m + m - j];
	 	if(Hash.find(tmp) == true)
	 	return true;
	 	Hash.add(tmp);
	 }
	 return false;
}
int main()
{
	while(~scanf("%d %d",&n,&m))
	{
		
		for(int i = 1;i <= n;i++) scanf("%s",s[i]+1);
		init();
		int l = 1,r=min(n,m),mid,ans = -1;
		while(l<=r)
		{
			mid = (l + r) >> 1;
			if(check(mid))
			l = mid + 1,ans = mid;
			else
			r = mid - 1;
		}
		printf("%d\n",ans);
	}
	return 0;
}





你可能感兴趣的:(后缀数组)