背景
某不愿透露姓名的czyarl同学最近学了一种很有趣的算法,名叫SAM。
他很高兴啊,就拿着这个东西去刷题。
他上了某谷,把板子过掉了,很开心。
然后他一搜,不对啊,怎么那么多紫题。
于是他就去刷紫题。
刷了两道,他就膨胀了,去刷黑题。喏,就这道LuoguP4022
他一看,这不是二分答案再用单调队列优化dp求是否可行嘛,水!写到一半,他才发现,自己由于兹思sui平不够,已经不会写单调队列了。就去学习了一遍单调队列,刷了一道绿题。(事实是,他至今一道都没写过,此前都是听听图个乐呵)
然后,他就回去写了P4022。他自信满满地坐在电脑面前,屏幕的微光混着机房外的阳光,平和地洒在他的脸上。他显着一副雀跃的样子,手操纵着鼠标,缓缓地,轻轻地点上了提交。他看着那状态,由深蓝的wait,变到浅蓝的judging,直到最后,变成了,一个红红的unaccepted。
观察,他发现,自己t了两个点。而其他的点都跑得飞快。
不解。考虑到自己的SAM是封装起来的,他决定把SAM拆下来。
依旧T。
不解。考虑到自己可能是读入太奇怪,全都换成了getchar,而且读进来,能丢掉就丢掉。
依旧T。
不解。他决定,看看别人的代码。却发现根本没什么区别。
最后,他看到了那个在二分后判断答案是否可行时的memset。他发现,如果每一次的字符串特别短,但是个数特别多,自己就烂了。
去掉之后,他A了。
唔,用第三人称写还挺顺手的。
刚刚讲的例子
这是我一开始
#include<cstdio>
#include<iostream>
#include<cstring>
#define MAXN 1100000
template<typename T>inline 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;
}
template<typename T>inline 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<r-1)
{
int zhong = (l+r)>>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<cstdio>
#include<iostream>
#include<cstring>
#define MAXN 1100000
template<typename T>inline 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;
}
template<typename T>inline 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<r-1)
{
int zhong = (l+r)>>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.数据千万条,清零第一条;多测不清空,爆零两行泪。我们一定要在最恰当的时候清零。