NOIP 模拟 929

匹配

有两个字符串A,B,其中B是A的一个长度为$l_{B}$前缀,现在给B末尾填上字符,求A的最大前缀等于B的后缀

$T<=10,l_{B}<=100000,l_{B}<=l_{A}<=2*l_{B}$,所有字母均为小写字母

题解

考虑到B除了最后一个字符就是A的前缀,所以如果变换后的B不是A的前缀,那么求B的最大前缀等于后缀即可。

用kmp就行了

NOIP 模拟 929
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

const int maxn=200005;
int T,lena,lenb;
char c[2],s[maxn],t[maxn];
int fail[maxn];
void kmp(){
    fail[0]=fail[1]=0;
    for(int i=1;i<lenb;i++){
        int t=fail[i];
        while(t&&s[i]!=s[t]) t=fail[t];
        fail[i+1]=(s[i]==s[t] ? t+1 : 0);
    }
}

int main(){
    freopen("string.in","r",stdin);
    freopen("string.out","w",stdout);
    scanf("%d",&T);
    while(T--){
        scanf("%d%d%s%s",&lena,&lenb,s,c);
        if(s[lenb]==c[0]) {printf("%d\n",lenb+1);continue;}
        s[lenb++]=c[0];
        kmp();
        printf("%d\n",fail[lenb]);
    }
}
string

回家

有一个n个点m条边的图,求从1到n的必经点。

m<=2n,n<=200000,多组数据

题解

给一种错误思路:考虑必经点一定是割点,必经点一定在最短路上。所以tarjan求出割点之后,跑两遍bfs求最短路,最后判断。

为什么会错呢?因为这些只是性质,并不能推出结论。

最主要的就是不能保证这个割点能使1和n不连通

所以考虑在tarjan的时候记录n是否在子树中,判断即可。

细节在代码注释

NOIP 模拟 929
#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

const int maxn=200005;
const int inf=0x3f3f3f;
int t,n,m,cur,cnt;
int head[maxn];
int dfn[maxn],low[maxn];
int top,sta[maxn];
bool cut[maxn],con[maxn];
int dis[maxn][2];
struct edge{
    int x,y,next;
}e[maxn<<3];

template<class T>inline void read(T &x){
    x=0;int f=0;char ch=getchar();
    while(!isdigit(ch)) {f|=(ch=='-');ch=getchar();}
    while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    x = f ? -x : x ;
}

void add(int x,int y){
    e[++cnt]=(edge){x,y,head[x]};
    head[x]=cnt;
}

void init(){
    cnt=top=cur=0;
    memset(cut,false,sizeof(cut));
    memset(con,false,sizeof(con));
    memset(head,0,sizeof(head));
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    for(int i=1;i<=n;i++)
     dis[i][0]=dis[i][1]=inf;
}

void tarjan(int x,int fa){
    int num=0;if(x==n) con[x]=true;
    low[x]=dfn[x]=++cur;
    for(int i=head[x];i;i=e[i].next){
        int y=e[i].y;
        if(!dfn[y]){
            tarjan(y,x);
            low[x]=min(low[x],low[y]);
            con[x]|=con[y];
            if(dfn[x]<=low[y]&&con[y])//判断con[y]:当因为dfn[x]<=low[y]知道是割点的时候会y的子树分开,如果最后判断x会造成分开的子树不含n 
              cut[x]=true;
        }
        else if(y!=fa)  low[x]=min(low[x],dfn[y]);
    }
}

void solve(){
    read(n);read(m);
    init();
    for(int i=1;i<=m;i++){
        int x,y;
        read(x);read(y);
        if(x==y) continue;
        add(x,y);add(y,x);
    }
    tarjan(1,0);
    for(int i=2;i<n;i++)
     if(cut[i])
      sta[++top]=i;
    printf("%d\n",top);
    for(int i=1;i<=top;i++)
     printf("%d ",sta[i]);
    putchar(10);
}

int main(){
    freopen("home.in","r",stdin);
    freopen("home.out","w",stdout);
    read(t);
    while(t--)solve();
}
/*
2 
4 3
1 2
2 3
3 4
5 5
1 2
2 3
3 4
4 5
4 1
*/
home

 

上一篇:929.独特的电子邮件地址


下一篇:输入一个十六进制数a,和一个八进制数b,输出a+b的十进制结果