<转自原博客> 可爱的字符串算法们

非常强又非常关心学弟学妹学习的企鹅学长变态的考纲下,我们无奈中选择一起学习新姿势

first:KMP算法

这是一个小迪更过博客的算法,我就不好意思在这里献丑了,所以献上友链一份:http://rabbithu.xyz/index.php?title=2017-04-01-01

second:Trie树(字典树)

嘤嘤嘤,这就是我在oi小组讲的第一堂课了!?(虽然当天大家都很颓,但是算法的简单是毋庸置疑的!)

在有关字符串的问题中,我们会遇到一些子串啊~前缀啊~的问题,如果正常枚举遍历的话复杂度为O(n*m)(n、m为字串长度),obviously,这很慢!那怎么办呢?

这时候,就有一个十分聪明的人,想到了树!可是树和字符串有啥关系?看图就知道了!

上图这棵树中,边代表字母,结点存储是否是一个单词的结尾,这样我们是不是就可以通过一个26叉树存储任何想要的单词了w!

【如此可知,根节点代表一个空串】

此刻肯定已经有一些人在想怎样代码实现了,链前?显然,如果用链前存储的话,我们无论是插入还是查询都需要在每一层遍历当前字母,这样复杂度岂不是爆炸?所以我们就用一个简单机智的方法实现存儿子的过程!

struct node
{
int data;
int childs[26];
}tree[N];

Trie 的节点可以使用一个结构体进行存储,如下代码中,trans[i]表示这个节点边上字符为i 的转移边所到达的儿子节点编号,若为 0 则表示没有这个儿子。是不是特别喵啊!

插入:若对于一个字符串集合为小写的树中插入一个字符串s

事实上我们只要用O(len)的复杂度,一次存入字母边(检查如果有这条边存在,那么直接下一个,否则建边即可),并且在最后标记是单词的结尾就好了!
void insert()
{
	int l=strlen(num),now=1;
	for (int i=0;i<l;i++)
	{
		if (!tree[now].childs[num[i]-‘a‘])
		tree[now].childs[num[i]-‘0‘]=tot++;
		now=tree[now].childs[num[i]-‘a‘];
	}
	tree[now].end=1;
	return ;
}

查询:查询一个字符串 S 是否是给定字符串集合中某个串的前缀: 这有什么好说的?不和插入是一样的吗!如果没有这条边直接return false就好啊【不附代码了,哼唧!】 最后送上一道例题:POJ 3630 //其中用到了插入、查询同时进行的复杂度优化,简单易懂

#include<cstdio>
#include<cstring>
#define M 11
using namespace std;
const int N=1e5+5;
int n,t,tot,ans;
char num[M];
struct hhh
{
	bool end;
	int childs[M];
	void clear()
	{
		memset(childs,0,sizeof(childs));
		end=false;
	}
}tree[N];

bool insert()
{
	int l=strlen(num),now=1;
	for (int i=0;i<l;i++)
	{
		if (!tree[now].childs[num[i]-‘0‘])
		{
			tree[now].childs[num[i]-‘0‘]=tot++;
			tree[tot-1].clear();
		}
		else if (i==l-1)  return 0;
		now=tree[now].childs[num[i]-‘0‘];
		if (tree[now].end)  return 0;
	}
	tree[now].end=1;
	return 1;
}

int main()
{
	scanf("%d",&t);
	while (t--)
	{
		tot=1;
		ans=1;
		tree[tot++].clear();
		scanf("%d",&n);
		while (n--)
		{
			scanf("%s",num);
			if (!insert()) ans=0;
		}
		if (ans)
		printf("YES\n");
		else printf("NO\n");
	}
	return 0;
}

如果有小伙伴看到这里,那你可以去尝试一下poj2001 如果没算错的话,只能用指针来实现Trie树 代码如下:

#include<cstdio>
#include<cstring>
#define N 1111
#define M 22
using namespace std;
int n,i;
char ha[N][M];
struct node
{
	int count;
    node *childs[26];
    node()
    {
        count=0;
        int i;
        for(i=0;i<26;i++)
        childs[i]=NULL;
    }
};
node *root = new node;
node *now,*newnode;

void insert(int x)
{
	int l=strlen(ha[x]);
	now=root;
	for (int j=0;j<l;j++)
	{
		if (now->childs[ha[x][j]-‘a‘])
		{
			now=now->childs[ha[x][j]-‘a‘];
			++(now->count);
		}
		else
		{
			newnode=new node;
			++(newnode->count);
			now->childs[ha[x][j]-‘a‘]=newnode;
			now=newnode;
		}
	}
	return ;
}

void search(int x)
{
	now=root;
	int l=strlen(ha[x]),cnt=0;
	char ans[M];
	for (int j=0;j<l;j++)
	{
		now=now->childs[ha[x][j]-‘a‘];
		ans[cnt++]=ha[x][j];
		ans[cnt]=‘\0‘;
		if (now->count==1)
		{
			printf("%s %s\n",ha[x],ans);
			return ;
		}
	}
	printf("%s %s\n",ha[x],ans);
	return ;
}

int main()
{
	while (~scanf("%s",ha[i])) insert(i++);
	for (int j=0;j<i;j++) search(j);
	return 0;
}

我觉得不会有人看到这里了…… 以下是20160603模拟easy round1 T2 震惊

Description “震惊,OIer熬夜学习可持久化tire树竟是因为……” ——企鹅头条 钫企鹅听说哦艾尔要加入企鹅头条,书写传奇新闻,夺得普利鹅2333年新闻奖。便让他去震惊部去历练一下。 震惊有一些不同的写法,均由小写字母构成,且长度不超过 100100 。比如 : shock,choc,schock 等。每一种震惊的写法,在震惊部拥有不同的权值。 给定 nn 种震惊写法初始的权值,并进行 mm 个操作。 操作分两种 : 1.修改 : 给出一种震惊的写法,若这种写法存在 , 则将这种震惊写法的权值修改为 xx 。 2.询问 : 在 xx 次操作前,某种震惊写法的权值。即若当前为第 dd 次操作 , 则询问第 d?xd?x 次操作后 , 该种震惊写法的权值 。 注意:本题采用强制在线 , 请注意输入输出格式 Input 第一行两个整数 nn , mm 。 接下来 nn 行每行一个字符串 SS ,和一个整数 xx , 分别代表一种震惊的写法和该种写法的初始权值 。 接下来 mm 行每行一个字符串 SS 和一个整数 xx 。字符串 SS 代表一种震惊的写法。 如果本次操作是修改, xx 代表将这种震惊的写法的权值修改为 xx 。 如果本次操作是询问, xx 代表询问 xx 次操作前,这种震惊写法的权值 。 本题采用强制在线,若上次操作的输出为 Er 或 Shock ,则代表本次操作为询问,否则代表本次操作为修改。规定第 11 次操作为修改 。 Output 输出共 mm 行。每行对应一次操作。 对于每组修改,如果修改成功(即存在对应的震惊写法),输出 Ac 。如果不存在对应的震惊写法,输出 Er 。 对于每组询问,如果存在对应的震惊写法,输出对应 xx 次操作前,某种震惊写法的权值 xx 。如果不存在对应的震惊写法,输出 Shock 。

题解:暂割

AC代码:

#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#define M 1111
#define N 11111
#define inf 1111111
using namespace std;
int n,m,x,b,pre,j;
char a[M];
struct ha
{
	int time,dt;
	bool operator < (ha j) const
	{
		return time<j.time;
	}
	ha()
	{
		time=inf;
		dt=0;
	}
};
struct hhh
{
	vector <ha> data;
	bool jy;
	hhh *childs[30];
	hhh()
	{
		for(int i=0; i<26; i++)
			childs[i]=NULL;
		jy=0;
	}
};
hhh *root=new hhh;
hhh *now,*newnode;

void insert(int y)
{
	int l=strlen(a);
	now=root;
	for (int i=0; i<l; i++)
	{
		if (now->childs[a[i]-‘a‘])
			now=now->childs[a[i]-‘a‘];
		else
		{
			newnode=new hhh;
			now->childs[a[i]-‘a‘]=newnode;
			now=newnode;
		}
	}
	ha u;
	u.time=0;
	u.dt=y;
	now->data.push_back(u);
	now->jy=1;
	return ;
}

void query(int y)
{
	now=root;
	int l=strlen(a);
	for (int i=0; i<l; i++)
	{
		if (now->childs[a[i]-‘a‘])
			now=now->childs[a[i]-‘a‘];
		else
		{
			printf("Shock\n");
			pre=0;
			return ;
		}
	}
	if (!(now->jy))
	{
		printf("Shock\n");
		pre=0;
		return ;
	}
	if (j-y>=0)
	{
		vector <ha> :: iterator it;
		ha uu;
		uu.time=j-y;
		it=upper_bound(now->data.begin(),now->data.end(),uu);
		it--;
		printf("%d\n",it -> dt);
	}
	else
	{
		int v=now->data.size()-1;
		printf("%d\n",now->data[v].dt);
	}
	pre=1;
	return ;
}

void change(int y)
{
	int l=strlen(a);
	now=root;
	for (int i=0; i<l; i++)
	{
		if (now->childs[a[i]-‘a‘])
		{
			now=now->childs[a[i]-‘a‘];
		}
		else
		{
			printf("Er\n");
			pre=0;
			return ;
		}
	}
	if (!(now->jy))
	{
		printf("Er\n");
		pre=0;
		return ;
	}
	printf("Ac\n");
	pre=1;
	ha u;
	u.time=j;
	u.dt=y;
	now->data.push_back(u);
	return ;
}

int main()
{
	freopen("2(2).in","r",stdin);
	freopen("2.out","w",stdout);
	scanf("%d%d",&n,&m);
	pre=1;
	while(n--)
	{
		scanf("%s %d",a,&b);
		insert(b);
	}
	for (j=1; j<=m; j++)
	{
		scanf("%s %d",a,&x);
		if (pre) change(x);
		else query(x);
	}
	return 0;
}
时间: 2024-11-02 01:50:17

<转自原博客> 可爱的字符串算法们的相关文章

[原博客] BZOJ 2242 [SDOI2011] 计算器

题目链接 noip级数论模版题了吧.让求三个东西: 给定y,z,p,计算Y^Z Mod P 的值. 给定y,z,p,计算满足xy≡ Z ( mod P )的最小非负整数. 给定y,z,p,计算满足Y^x ≡ Z ( mod P)的最小非负整数. 其中P均为素数.来分着处理. 1 y^z%p 快速幂.推荐一种又快又好写的写法. 1 LL power_mod(LL a,LL b,LL p){ //get a^b%p 2 LL ret=1; 3 while(b){ 4 if(b&1) ret = re

[原博客] POJ 1067 取石子游戏

题目链接有两堆石子,数量任意,可以不同.游戏开始由两个人轮流取石子.游戏规定,每次有两种不同的取法,一是可以在任意的一堆中取走任意多的石子:二是可以在两堆中同时取走相同数量的石子.最后把石子全部取完者为胜者.现在给出初始的两堆石子的数目,如果轮到你先取,假设双方都采取最好的策略,问最后你是胜者还是败者.(中文题面,感动ing) 但是这道题实在是呵呵.开始没啥思路,根据必胜状态必败状态的定义,n^3打了个表,看起来是这样的. 图为100x100,已经缩小,左上角是状态(0,0),右下角状态为(10

[原博客] 组合游戏学习

阅读了<由感性认识到理性认识——透析一类搏弈游戏的解答过程>.<解析一类组合游戏>.<组合游戏略述——浅谈SG游戏的若干拓展及变形>这三篇论文,对组合游戏以及SG函数有了更深的理解.这篇文章摘下了这三篇论文的部分重要内容,以及部分我对组合游戏的理解. 一些名词与约定: 游戏:这里的游戏指的并不是平时玩的那些游戏(Dota2啥的),而是只一些如Nim取石子之类的“益智”组合游戏.并且,我们关注的不是游戏好不好玩,而是游戏有没有必胜策略.下文详细介绍. 状态:用一些数字来表

[原博客] HEOI2014 行记

HEOI: 河北省信息学竞赛省队选拔赛 又到了一年一度的HEOI呢. 我果然还是太弱了呢. (省选是在5月17.18日举行,日志是后补的..) Day0 报到日啊,省选今年在河北经贸大学举办,还算很便利的.于是下午翘了一节语文课,出校门打了辆车,就去了. 我跟那位司机说河北经贸大学,他说“是五七路那个么”,我说就那个红旗大街附近那个.然后就直奔五七路了.....走到一半左右,在手机GPS上发现自己已经偏离了方向,就跟司机说啊,于是发现走错了路,原路返回..(打车花了好多钱. )最后竟然按时到了报

[原博客] POI系列(1)

正规.严谨.精妙. -POI 发现POI(波兰信息学奥赛)的题都很有意思.于是开刷bzoj上的poi题目(按ac人数降序..).顺手写一写题解,加深印象. 为了防止一篇文章过于长,打算每五道题另起一篇文章. BZOJ 1103 : [POI2007]大都市meg 给一棵树,每次可以把树上的一些边标记了,问一个点与根之间需要走多少没有标记的边. 这个可以把这颗树遍历得到一个dfs序每第一次经过一个点的时候记录为+1,第二次经过一个点的时候记录为-1,然后记录每个点第一次经过时在序列里的位置f[i]

[原博客] POJ 2975 Nim 统计必胜走法个数

题目链接题意介绍了一遍Nim取石子游戏,可以看上一篇文章详细介绍.问当前状态的必胜走法个数,也就是走到必败状态的方法数. 我们设sg为所有个数的Xor值.首先如果sg==0,它不可能有必胜走法,输出0. 对于任意一堆有a[i]个石子,若sg Xor a[i] <= a[i] ,那么我们就可以在a[i]里面取出sg Xor a[i]个石子,使得剩下石子Xor和为0,于是ans++.然后输出ans. 注意C/C++语言中^操作比<操作优先级低. #include<iostream> #

[原博客] 关于线性筛

埃氏筛法:从2开始,找到第一个没有被筛的数,把它标记为素数,然后把它的2倍.3倍……筛掉.复杂度O(nlogn). 改进的埃氏筛法:从2开始,找到第一个没有被筛的数x,把它标记为素数,然后把它的x倍.x+1倍……筛掉.复杂度O(nloglogn). 线性筛:保证每个数都被它的最小素因子筛掉.复杂度O(n). C++写起来大概是这样的: int mindiv[10000005],tot,prime[10000050]; int main(){ for(int i=2;i<=10000000;i++

[原博客] POJ 1704 Georgia and Bob

题目链接题意:如图,Georgia和Bob在玩游戏.一个无限长的棋盘上有N个旗子,第i个棋子的位置可以用Pi表示.现在Georgia先走.每个人每一次可以把一枚棋子向左移动任意个格子,但是不能超越其他棋子,也不能和其他棋子处在同一个格子里.如果轮到某一个人的时候Ta再也不能移动棋子了,就判负.现在每个测试数据给定一种情况,如果Georgia会赢,输出“Georgia will win”,如果Bob会赢,输出“Bob will win”,如果不确定,输出“Not sure”.两个人都知道获胜策略是

[原博客] POJ 2505 A multiplication game 组合游戏

题目链接题意: 有一个数p=1,甲乙两人轮流操作,每次可以把p乘2~9中的一个数,给定一个n,当一个人操作后p>=n,那么这个人赢,问先手是否必胜. 必胜状态:存在一种走法走到一个必败状态. 必败状态:后继状态都为必胜状态. 我们可以知道>=n的数都为必败状态,可以转移到>=n的最小的数为n/9(上取整),所以 n/9~n-1都为必胜态,同理n/9/2(都为上取整)为最小的必须转移到n/9~n-1(必胜状态)的状态,所以n/9/2~n/9-1为必败态,于是就可以这样推到1,看一下1是必胜