子串的个数
给你一个由小写字母“a,b,c,d,e”构成的字符串S,请找出S中包含了多少个不同的长度为N的子串。例如,给出的字符串是"daababac",N的值为3,那么长度为3的不同子串有"daa"; "aab"; "aba"; "bab"; "bac",最后输出的结果是5。
一开始给你一个空字符串S,有两个操作需要你完成:
编号“1”:在S后添加一个字符串S’,(也就是字符串加法S=S+S’);
编号“2”:输出当前S中包含的不相同的长度为N的字符串的个数;
输入格式:
第一行,一个整数N
第二行,一个整数M,表示下面有M个操作
接下来M行,每行表示一个操作(1号操作为数字1,然后是一个空格,接着是一个字符串,2号操作只有一个数字2)。
输出格式:
对于每个编号2的操作,输出一整数,表示所求结果。
样例输入1: | 样例输入2: |
3 4 1 daababac 2 1 acc 2 | 4 5 1 ebaedabedaedbaedaedbaedbaeadeaccccccc 1 ddddd 2 1 abcd 2 |
样例输出1: | 样例输出2: |
5 8 | 24 28 |
数据范围:
对于 50% 的数据:1≤M≤100 1≤字符串S最终的长度≤10,000 1≤N≤8
对于 100% 的数据:1≤M≤1000 1≤字符串S最终的长度≤200,000 1≤N≤8
—————————————————————————————————————————————————————————————————————————————
这道题有很多种解法,简记一下。
1.trie树;
=========================
这是一颗神奇的树!
注意从原点0 开始,漫漫坑跌路,被坑了几次了!
但毫无疑问,这是最快的。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
using namespace std;
struct node{int s[5];};
node trie[200005];
char B[200005],A[200005];
int n,m,now,cnt,tot;
void insert(char a[10])
{
int i,p=0;
bool mark=0;
for(i=1;i<=n;i++)
{
int t=a[i]-'a';
if(trie[p].s[t])p=trie[p].s[t];
else
{
tot++;mark=1;
p=trie[p].s[t]=tot;
}
}
if(mark)cnt++;
}
int main()
{
// freopen("data7.in","r",stdin);
// freopen("data.out","w",stdout);
int i,j,k,t,p=1,q,l;
scanf("%d%d",&n,&m);
for(i=1;i<=m;i++)
{
scanf("%d",&k);
if(k==1)
{
scanf("%s",A+1);
A[0]=' ';
l=strlen(A)-1;
q=1;
while(q<=l)
{
for(j=p;j<=n;j++)
{
B[j]=A[q++];
// printf("-%c-",B[j]);
}
// for(j=1;j<=n;j++)printf("-%c-",B[j]);
// putchar(10);
B[0]=' ';
// for(j=1;j<=n;j++)printf("%c",B[j]);
if(j==n+1)
{
insert(B);
for(j=1;j<n;j++)B[j]=B[j+1];
p=n;
}
}
}
if(k==2)printf("%d\n",cnt);
}
// system("pause");
return 0;
}
2.hash;
=========================
我的hash写的很渣。
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
using namespace std;
string a="",b,s;
bool mark[17000000];
unsigned int hash(string s)
{
unsigned int hash=0,seed=131;
unsigned int i=0,len=s.length();
while(i<len)hash=hash*seed+s[i++];
return (hash&0xFFFFFF);
}
int main()
{
int n,m,j,len,k,t,tot=0,i;
scanf("%d%d",&n,&m);
len=n-1;
for(i=1;i<=m;i++)
{
scanf("%d",&k);
if(k==1)
{
cin>>b;
a+=b;
for(j=len-n+1,len=a.length();j+n<=len;j++)
{
s=a.substr(j,n);
t=hash(s);
if(!mark[t])
{
tot++;
mark[t]=1;
}
}
}
else printf("%d\n",tot);
}
// system("pause");
return 0;
}
3.神奇的map;
=========================
万万没想到,唉,to young to simple;
但事实告诉我们,手写的确实要快一些。
#include<cstdio>
#include<iostream>
#include<map>
#include<cstring>
using namespace std;
map<string,int>mp;
string a="",b;
int main()
{
int n,m,i,j,len,k;
scanf("%d%d",&n,&m);
len=n-1;
for(i=1;i<=m;i++)
{
scanf("%d",&k);
if(k==1)
{
cin>>b;
a+=b;
for(j=len-n+1,len=a.length();j+n<=len;j++)
mp[a.substr(j,n)]++;
}
else printf("%d\n",mp.size());
}
return 0;
}
4.更神奇的5进制;
这不给我说,打死都想不到。
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
using namespace std;
bool mark[500000];
int Mod[]={1,5,25,125,625,3125,15625,78125,390625};
string a="",b,s;
int n;
int change(string s)
{
int tot=0;
for(int i=0;i<n;i++)tot+=(s[i]-'a')*Mod[i];
return tot;
}
int main()
{
int k,m,i,j,len,t,tot=0;
scanf("%d%d",&n,&m);
len=n-1;
for(i=1;i<=m;i++)
{
scanf("%d",&k);
if(k==1)
{
cin>>b;
a+=b;
for(j=len-n+1,len=a.length();j+n<=len;j++)
{
s=a.substr(j,n);
t=change(s);
if(!mark[t])
{
tot++;
mark[t]=1;
}
}
}
else printf("%d\n",tot);
}
return 0;
}
(完)