Bazinga
题意:给你n个字符串,问你一个最大的i,使得前i-1个字符串至少有一个不是这个串的子串,如果不存在,输出-1。队友用kmp+链表优化过的,虽然不懂他的思路,不过模拟赛的时候A了也没多想。
当时我的思路是:对所有的字符串按长度排序,这样可以优化长度乱序的情况,然后比较相邻两个,如果前一个不是后一个的子串就把前一个串存起来,更新答案,每次比较先与前一个比较,符合的话再和存起来的串比较,不符合把前一个存起来,更新答案。
代码一:只存一个 WA。
const int N=2e6+10;
void kmp_pre(char x[],int m,int next[])
{
int i,j;
j=next[0]=-1;
i=0;
while(i<m)
{
while(-1!=j&&x[i]!=x[j]) j=next[j];
next[++i]=++j;
}
}
int nex[2010];
bool judge(char x[],char y[])
{
int i=0,j=0;
int m=strlen(x);
int n=strlen(y);
kmp_pre(x,m,nex);
while(i<n)
{
while(-1!=j&&y[i]!=x[j]) j=nex[j];
i++,j++;
if(j>=m) return true;
}
return false;
}
char s[501][2010];
int main()
{
int t,n;
scanf("%d",&t);
int t1=t;
while(t--)
{
scanf("%d",&n);
for(int i=1; i<=n; i++) scanf("%s",s[i]);
int tmp=-1,ans=-1;
for(int i=2; i<=n; i++)
{
if(!judge(s[i-1],s[i]))
{
ans=i;
tmp=i-1;
}
else
{
if(tmp!=-1&&!judge(s[tmp],s[i])) ans=i;
}
// printf("i=%d ans=%d\n",i,ans);
}
printf("Case #%d: %d\n",t1-t,ans);
}
return 0;
}
代码二:把所有不符合的存起来,暴力判 746ms AC:
const int N=2e6+10;
void kmp_pre(char x[],int m,int next[])
{
int i,j;
j=next[0]=-1;
i=0;
while(i<m)
{
while(-1!=j&&x[i]!=x[j]) j=next[j];
next[++i]=++j;
}
}
int nex[2010];
bool judge(char x[],char y[])
{
int i=0,j=0;
int m=strlen(x);
int n=strlen(y);
kmp_pre(x,m,nex);
while(i<n)
{
while(-1!=j&&y[i]!=x[j]) j=nex[j];
i++,j++;
if(j>=m) return true;
}
return false;
}
char s[501][2010];
int tmp[501];
int main()
{
int t,n;
scanf("%d",&t);
int t1=t;
while(t--)
{
scanf("%d",&n);
for(int i=1; i<=n; i++) scanf("%s",s[i]);
int num=0,ans=-1;
for(int i=2; i<=n; i++)
{
if(!judge(s[i-1],s[i]))
{
ans=i;
tmp[num++]=i-1;
}
else
{
for(int j=0; j<num; j++)
if(!judge(s[tmp[j]],s[i]))
{
ans=i;
break;
}
}
}
printf("Case #%d: %d\n",t1-t,ans);
}
return 0;
}
strstr函数却更优,546ms AC:
strstr原理:字符串匹配two way算法 复杂度O(1)啊。
char s[501][2010];
int tmp[501];
int main()
{
int t,n;
scanf("%d",&t);
int t1=t;
while(t--)
{
scanf("%d",&n);
for(int i=1; i<=n; i++) scanf("%s",s[i]);
int num=0,ans=-1;
for(int i=2; i<=n; i++)
{
// if(!judge(s[i-1],s[i]))
if(strstr(s[i],s[i-1])==NULL)
{
ans=i;
tmp[num++]=i-1;
}
else
{
for(int j=0; j<num; j++)
// if(!judge(s[tmp[j]],s[i]))
if(strstr(s[i],s[tmp[j]])==NULL)
{
ans=i;
break;
}
}
}
printf("Case #%d: %d\n",t1-t,ans);
}
return 0;
}
Meeting
近阶段做过最有意思的一个题,目前见过所有图论中很有意思的一个题,被网络流的思想缠绕了很久,最后才想明白建图然后一发板子AC。
题意:两个人分别在1号点和n号点,有m个集合,每个集合里有Si个点,任意两个点之间需要Ti的时间到达。给你n,然后m个集合的信息,求两个人同时出发到一个点的最短时间,如果最短时间有多种方案,从小到大输出这些可行点。思路:由数据范围可以推测任意两个之间建边是行不通的,于是我们可以把每个集合虚拟一个点,虚拟点到这个集合的每个点的长度为Ti,点到虚拟点的长度为0。从1出发跑一遍最短路,从n出发跑一遍最短路,每个点上所需的时间就是1号点到这个点的时间与n号点到这个点时间的最大值。
建边的思想很强,当时想着用网络流跑费用流,但这个可行点却不知道怎么求,但将网络流的建图思想用到最短路上,getAC。
const int N=2e6+10;
struct Edge
{
int to,next;
ll w;
} e[N*2];
int n,m,tot,cnt,head[N],vis[N];
ll d1[N],d2[N];
struct node
{
int v;
ll c;
friend bool operator <(node a,node b)
{
return a.c>b.c;
}
};
priority_queue<node>q;
void init()
{
for(int i=1; i<=cnt; i++) d1[i]=d2[i]=INF;
d1[1]=0,d2[n]=0;
}
void add(int u,int v,ll w)
{
e[tot].to=v,e[tot].w=w;
e[tot].next=head[u];
head[u]=tot++;
}
void dij(int s,int f)
{
while(!q.empty()) q.pop();
memset(vis,0,sizeof(vis));
if(f==1) q.push(node {s,d1[s]});
else q.push(node {s,d2[s]});
while(!q.empty())
{
node tmp=q.top();
q.pop();
int x=tmp.v;
if(vis[x]) continue;
vis[x]=1;
for(int i=head[x]; i+1; i=e[i].next)
{
int v=e[i].to;
ll w=e[i].w;
if(f==1)
{
if(d1[v]>d1[x]+w)
{
d1[v]=d1[x]+w;
q.push(node {v,d1[v]});
}
}
else
{
if(d2[v]>d2[x]+w)
{
d2[v]=d2[x]+w;
q.push(node {v,d2[v]});
}
}
}
}
}
int SA[N];
int main()
{
int t;
scanf("%d",&t);
int t1=t;
while(t--)
{
scanf("%d%d",&n,&m);
tot=0,cnt=n;
memset(head,-1,sizeof(head));
int ti,num,x;
for(int i=1; i<=m; i++)
{
scanf("%d%d",&ti,&num);
++cnt;
for(int j=0; j<num; j++)
{
scanf("%d",&x);
add(cnt,x,ti);
add(x,cnt,0);
}
}
init();
dij(1,1);
dij(n,2);
ll ans=INF;
int AAA=0;
for(int i=1; i<=n; i++)ans=min(ans,max(d1[i],d2[i]));
for(int i=1; i<=n; i++)
{
ll TT=max(d1[i],d2[i]);
if(TT==ans) SA[AAA++]=i;
}
printf("Case #%d: ",t1-t);
sort(SA,SA+AAA);
if(ans==INF) puts("Evil John");
else
{
printf("%lld\n",ans);
for(int i=0; i<AAA; i++)
printf("%d%c",SA[i],i==AAA-1?'\n':' ');
}
}
return 0;
}