【刷题笔记】关于memset的几点注意事项

背景

某不愿透露姓名的czyarl同学最近学了一种很有趣的算法,名叫SAM。

他很高兴啊,就拿着这个东西去刷题。

他上了某谷,把板子过掉了,很开心。

然后他一搜,不对啊,怎么那么多紫题。

于是他就去刷紫题。

刷了两道,他就膨胀了,去刷黑题。喏,就这道LuoguP4022

他一看,这不是二分答案再用单调队列优化dp求是否可行嘛,水!写到一半,他才发现,自己由于兹思sui平不够,已经不会写单调队列了。就去学习了一遍单调队列,刷了一道绿题。(事实是,他至今一道都没写过,此前都是听听图个乐呵

然后,他就回去写了P4022。他自信满满地坐在电脑面前,屏幕的微光混着机房外的阳光,平和地洒在他的脸上。他显着一副雀跃的样子,手操纵着鼠标,缓缓地,轻轻地点上了提交。他看着那状态,由深蓝的wait,变到浅蓝的judging,直到最后,变成了,一个红红的unaccepted。

观察,他发现,自己t了两个点。而其他的点都跑得飞快。

不解。考虑到自己的SAM是封装起来的,他决定把SAM拆下来。

依旧T。

不解。考虑到自己可能是读入太奇怪,全都换成了getchar,而且读进来,能丢掉就丢掉。

依旧T。

不解。他决定,看看别人的代码。却发现根本没什么区别。

最后,他看到了那个在二分后判断答案是否可行时的memset。他发现,如果每一次的字符串特别短,但是个数特别多,自己就烂了。

去掉之后,他A了。

唔,用第三人称写还挺顺手的。

刚刚讲的例子

这是我一开始

#include
#include
#include
#define MAXN 1100000
templateinline void Read(T &cn)
{
	char c;int sig = 1;
	while(!isdigit(c = getchar()))if(c == '-')sig = -1;cn = c-48;
	while(isdigit(c = getchar()))cn = cn*10+c-48;cn*=sig;
}
templateinline void Write(T cn)
{
	if(!cn){putchar('0');return;}
	if(cn<0){putchar('-');cn = 0-cn;}
	int wei = 0;T cm = 0;int cx = cn%10;cn=cn/10;
	while(cn)wei++,cm = cm*10+cn%10,cn=cn/10;
	while(wei--)putchar(cm%10+48),cm=cm/10;
	putchar(cx+48);
}
struct SAM{
	struct node{
		int len,link;
		int ch[3];
	};
	node t[MAXN*2+10];
	int tlen;
	int last;
	inline void build()
	{
		last = tlen = 1;
		t[1].len = t[1].link = 0;
		memset(t[1].ch,0,sizeof(t[1].ch));
	}
	inline void jia(char c)
	{
		int lin = c-'0';
		int cur = ++tlen;
		t[cur].len = t[last].len+1;
		memset(t[cur].ch,0,sizeof(t[cur].ch));
		int p = last;
		for(;p && !t[p].ch[lin];p = t[p].link)t[p].ch[lin] = cur;
		if(!p)t[cur].link = 1;
		else{
			int q = t[p].ch[lin];
			if(t[q].len == t[p].len+1)t[cur].link = q;
			else{
				int cln = ++tlen;
				t[cln] = t[q];
				t[q].link = t[cur].link = cln;
				t[cln].len = t[p].len +1;
				for(;p && t[p].ch[lin] == q;p = t[p].link)t[p].ch[lin] = cln;
			}
		}
		last = cur;
	}
	inline void zou(char c[],int clen,int g[],int &da)
	{
		int dang = 1,cha = 0;
		da = 0;
		for(int i = 1;i<=clen;i++)
		{
			int lin = c[i]-'0';
			while(dang != 1 && !t[dang].ch[lin])dang = t[dang].link,cha = std::min(cha,t[dang].len);
			if(t[dang].ch[lin])dang = t[dang].ch[lin],cha++;
			g[i] = cha;
			da = std::max(da,cha);
		}
	}
};
SAM S;
int n,m;
char c[MAXN+1];
int clen;
int f[MAXN+1];
int g[MAXN+1];
int dui[MAXN+1];
int l,r;
inline void getit(char c[],int &clen)
{
	while(!isdigit(c[1] = getchar()));clen = 1;
	while(isdigit(c[++clen] = getchar()));clen--;
}
int check(int cn)
{
	dui[l = 1] = 0;
	r = 1;
	memset(f,0,sizeof(f));//THIS IS IMPORTANT.
	for(int i = cn;i<=clen;i++)
	{
		while(l <= r && dui[l] < i-g[i])l++;
		f[i] = f[i-1];
		if(l <= r)f[i] = std::max(f[i],f[dui[l]] + i - dui[l]);
		while(l <= r && f[dui[r]] - dui[r] < f[i-cn+1] - (i-cn+1))r--;
		dui[++r] = i-cn+1;
	}
	return f[clen]*10 >= clen*9;
}
int erfen(int cm)
{
	if(!check(1))return 0;
	int l = 1,r = cm+1;
	while(l>1;
		if(check(zhong))l = zhong;
		else r = zhong;
	}
	return l;
}
int main()
{
	S.build();
	Read(n);Read(m);
	char linc;
	for(int i = 1;i<=m;i++)
	{
		while(!isdigit(linc = getchar()));S.jia(linc);
		while(isdigit(linc = getchar()))S.jia(linc);
		S.jia('2');
	}
	for(int i = 1;i<=n;i++)
	{
		getit(c,clen);
		int lin = 0;
		S.zou(c,clen,g,lin);
		Write(erfen(lin));
		putchar('\n');
	}
	return 0;
}

的代码

这是我改完的代码

#include
#include
#include
#define MAXN 1100000
templateinline void Read(T &cn)
{
	char c;int sig = 1;
	while(!isdigit(c = getchar()))if(c == '-')sig = -1;cn = c-48;
	while(isdigit(c = getchar()))cn = cn*10+c-48;cn*=sig;
}
templateinline void Write(T cn)
{
	if(!cn){putchar('0');return;}
	if(cn<0){putchar('-');cn = 0-cn;}
	int wei = 0;T cm = 0;int cx = cn%10;cn=cn/10;
	while(cn)wei++,cm = cm*10+cn%10,cn=cn/10;
	while(wei--)putchar(cm%10+48),cm=cm/10;
	putchar(cx+48);
}
struct SAM{
	struct node{
		int len,link;
		int ch[3];
	};
	node t[MAXN*2+10];
	int tlen;
	int last;
	inline void build()
	{
		last = tlen = 1;
		t[1].len = t[1].link = 0;
		memset(t[1].ch,0,sizeof(t[1].ch));
	}
	inline void jia(char c)
	{
		int lin = c-'0';
		int cur = ++tlen;
		t[cur].len = t[last].len+1;
		memset(t[cur].ch,0,sizeof(t[cur].ch));
		int p = last;
		for(;p && !t[p].ch[lin];p = t[p].link)t[p].ch[lin] = cur;
		if(!p)t[cur].link = 1;
		else{
			int q = t[p].ch[lin];
			if(t[q].len == t[p].len+1)t[cur].link = q;
			else{
				int cln = ++tlen;
				t[cln] = t[q];
				t[q].link = t[cur].link = cln;
				t[cln].len = t[p].len +1;
				for(;p && t[p].ch[lin] == q;p = t[p].link)t[p].ch[lin] = cln;
			}
		}
		last = cur;
	}
	inline void zou(char c[],int clen,int g[],int &da)
	{
		int dang = 1,cha = 0;
		da = 0;
		for(int i = 1;i<=clen;i++)
		{
			int lin = c[i]-'0';
			while(dang != 1 && !t[dang].ch[lin])dang = t[dang].link,cha = std::min(cha,t[dang].len);
			if(t[dang].ch[lin])dang = t[dang].ch[lin],cha++;
			g[i] = cha;
			da = std::max(da,cha);
		}
	}
};
SAM S;
int n,m;
char c[MAXN+1];
int clen;
int f[MAXN+1];
int g[MAXN+1];
int dui[MAXN+1];
int l,r;
inline void getit(char c[],int &clen)
{
	while(!isdigit(c[1] = getchar()));clen = 1;
	while(isdigit(c[++clen] = getchar()));clen--;
}
int check(int cn)
{
	dui[l = 1] = 0;
	r = 1;
	memset(f,0,sizeof(l)*(cn+1));//THIS IS IMPORTANT..
	for(int i = cn;i<=clen;i++)
	{
		while(l <= r && dui[l] < i-g[i])l++;
		f[i] = f[i-1];
		if(l <= r)f[i] = std::max(f[i],f[dui[l]] + i - dui[l]);
		while(l <= r && f[dui[r]] - dui[r] < f[i-cn+1] - (i-cn+1))r--;
		dui[++r] = i-cn+1;
	}
	return f[clen]*10 >= clen*9;
}
int erfen(int cm)
{
	if(!check(1))return 0;
	int l = 1,r = cm+1;
	while(l>1;
		if(check(zhong))l = zhong;
		else r = zhong;
	}
	return l;
}
int main()
{
	S.build();
	Read(n);Read(m);
	char linc;
	for(int i = 1;i<=m;i++)
	{
		while(!isdigit(linc = getchar()));S.jia(linc);
		while(isdigit(linc = getchar()))S.jia(linc);
		S.jia('2');
	}
	for(int i = 1;i<=n;i++)
	{
		getit(c,clen);
		int lin = 0;
		S.zou(c,clen,g,lin);
		Write(erfen(lin));
		putchar('\n');
	}
	return 0;
}

可以看到,我只是把

memset(f,0,sizeof(f));

改为了

memset(f,0,sizeof(l)*(cn+1));

用人话说,我把清零操作变得精准了。此处我只要清第0位到第cn-1位,所以就清空了cn+1个int长度的空间。

其实应该是cn,不用cn+1。但是由于我心虚,还是加上了1。

然后那里的sizeof(l),l就是一个随便抓过来的int类型的变量,可以用4代替。若是long long,应用8代替。

初步总结

所以,我以后清空数组应该这么写:memset(f,0,sizeof(f[0])*(n+1));其中n是目标长度。

为什么会这样?

我们可以认为,memset是一个常数很小的操作。但是它仍需要对这O(n)个内存单元进行处理,所以它的本质还是O(n)的。

最后的提示

1.一时清零一时爽,一直清零t光光。

2.数据千万条,清零第一条;多测不清空,爆零两行泪。我们一定要在最恰当的时候清零。

你可能感兴趣的:(details)