Codeforces Round #288 (Div. 2) D. Tanya and Password (欧拉通路)

本文介绍了一道编程题目,涉及通过已知的连续三字母子串重建原始密码。利用图论中的欧拉通路概念来解决这一问题,并提供了两种不同的实现方法。

D. Tanya and Password
time limit per test
2 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

While dad was at work, a little girl Tanya decided to play with dad's password to his secret database. Dad's password is a string consisting of n + 2 characters. She has written all the possible n three-letter continuous substrings of the password on pieces of paper, one for each piece of paper, and threw the password out. Each three-letter substring was written the number of times it occurred in the password. Thus, Tanya ended up with n pieces of paper.

Then Tanya realized that dad will be upset to learn about her game and decided to restore the password or at least any string corresponding to the final set of three-letter strings. You have to help her in this difficult task. We know that dad's password consisted of lowercase and uppercase letters of the Latin alphabet and digits. Uppercase and lowercase letters of the Latin alphabet are considered distinct.

Input

The first line contains integer n (1 ≤ n ≤ 2·105), the number of three-letter substrings Tanya got.

Next n lines contain three letters each, forming the substring of dad's password. Each character in the input is a lowercase or uppercase Latin letter or a digit.

Output

If Tanya made a mistake somewhere during the game and the strings that correspond to the given set of substrings don't exist, print "NO".

If it is possible to restore the string that corresponds to given set of substrings, print "YES", and then print any suitable password option.

Sample test(s)
input
5
aca
aba
aba
cab
bac
output
YES
abacaba
input
4
abc
bCb
cb1
b13
output
NO
input
7
aaa
aaa
aaa
aaa
aaa
aaa
aaa
output
YES
aaaaaaaaa


题意:

有一个串的长度为n+2

现在给出这个串的n个子串,每个子串由3个字符组成。

字符可以是大小写字母和数字。

求原串。


分析:

将一个子串看成一条边,前2个字符看成节点1,后2个字符看成节点2。

节点1到节点2连边构造图,求欧拉通路。

统计每个节点的入度和出度。


判断是否存在欧拉通路的条件是:

1、原有向图忽略方向后联通(可dfs也可并查集,我用的是并查集)

2、abs(in[i]-out[i])==1的点  有0个或者2个


这里两点之间可能有重边,图中也可能有自环。

而且点有20万个。

我用vis[a][b]记录ab之间的边应该走的次数,dfs找路径时搜到一次减1,减到0就不能再走了


开始跑到第30组数据就TLE过不了了

卡住的数据是20万个zzz


然后我特判了一个字符的情况1000+ms才过- -

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cmath>
#include<vector>
#include<queue>
#include<map>

using namespace std;

typedef long long ll;

char s[5];
int in[4100],out[4100];
vector<int> g[4100];
int fa[4100],pp;
char ans[200010];
int vis[4100][4100];
bool have[4100];

int sb[4100];

int change(char x)
{
    int ret;
    if(x>='a' && x<='z')
        ret = x-'a';
    else if(x>='A' && x<='Z')
        ret = x-'A'+26;
    else ret = x-'0'+52;
    return ret;
}

char cha(int x)
{
    char ret;
    if(x>=0 && x<=25)
        ret = x+'a';
    else if(x>=26 && x<=51)
        ret = x-26+'A';
    else ret = x-52+'0';
    return ret;
}

void dfs(int st)
{
    for(int i=0;i<g[st].size();i++)
    {
        int r = g[st][i];
        if(!vis[st][r]) continue;
        vis[st][r]--;
        dfs(r);
    }
    int b;
    b = st%62;
    ans[pp++] = cha(b);
}

void init()
{
    for(int i=0;i<4000;i++)
        g[i].clear();
    memset(in,0,sizeof(in));
    memset(out,0,sizeof(out));
    memset(vis,0,sizeof(vis));
    memset(ans,0,sizeof(ans));
    memset(have,0,sizeof(have));
    for(int i=0;i<4005;i++) fa[i] = i;
    pp = 0;
}
int find(int x)
{
//    if(x == fa[x]) return x;
//    int t = find(fa[x]);
//    fa[x] = t;
//    return t;
    return fa[x]==x?x:(fa[x]=find(fa[x]));
}
void join(int x,int y)
{
    int fx = find(x);
    int fy = find(y);
    if(fx!=fy) fa[fx] = fy;
}
int main()
{
    int n,a,b,c,x,y;
    while(~scanf("%d",&n))
    {
        init();
        for(int i=0;i<n;i++)
        {
            scanf("%s",s);
            a = change(s[0]);
            b = change(s[1]);
            c = change(s[2]);
            x = a*62+b;
            y = b*62+c;
            vis[x][y]++;
            g[x].push_back(y);
            out[x]++;
            in[y]++;
            join(x,y);
            have[x] = true;
            have[y] = true;
        }
        int flag = 0,father = -1;
        int cnt = 0,pi;                 //特判一个字符的部分
        for(int i=0;i<4005;i++){        //
            if(have[i]){                //
                cnt++;                  //
                pi = i;                 //
            }                           //
        }                               //
        if(cnt==1){
            puts("YES");
            for(int i=1;i<=n+2;i++)
                printf("%c",cha(pi%62));
            printf("\n");
            continue;
        }
        for(int i=0;i<4005;i++)
        {
            if(!have[i]) continue;
            if(father==-1){
                father = find(i);
            }
            else
            {
                if(find(i)!=father)
                {
                    flag = 1;
                    break;
                }
            }
        }
        if(flag)
        {
            puts("NO");
            continue;
        }
        int cnt1 = 0,st = -1;
        for(int t=0;t<4005;t++)
        {
            if(abs(in[t]-out[t]) >1)
            {
                flag = 1;
                break;
            }
            if(abs(in[t]-out[t]) == 1)
            {
                if(out[t]-in[t]==1) st = t;
                cnt1++;
            }
        }
        if(flag || !(cnt1==0 || cnt1==2))
        {
            puts("NO");
            continue;
        }
        int sss,w;
        if(st!=-1) sss = st;
        else sss = father;
        dfs(sss);
        w = (sss-sss%62)/62;
        ans[pp++] = cha(w);
        if(pp!=n+2) puts("NO");
        else{
            puts("YES");
            for(int i=pp-1;i>=0;i--)
                printf("%c",ans[i]);
            printf("\n");
        }
    }
    return 0;
}

看了别人的代码  129ms就过了- -

存边是将 边计数和终点一起存到队列

然后搜的时候只记录边的序号。

代码来源:http://blog.youkuaiyun.com/sdj222555/article/details/43245421

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
#include <cmath>
#include <algorithm>
#include <map>
#include <ctime>
#define MAXN 111111
#define MAXM 1122222
#define INF 1000000007
#define eps 1e-8
using namespace std;
int n;
typedef pair<int, int> PII;
vector<PII> g[66 * 66];
int num[66 * 66];
int in[66 * 66], out[66 * 66];
int fa[66 * 66], v[66 * 66];
int en[66 * 66][66 * 66];
int vis[222222], nv[222222];
int st[222222];
char s[211111][6];
int find(int x) {
    if(x == fa[x]) return x;
    int t = find(fa[x]);
    fa[x] = t;
    return t;
}
void join(int x, int y) {
    int fx = find(x);
    int fy = find(y);
    if(fx != fy) fa[fx] = fy;
}
int getc(char c) {
    if(c >= 'a' && c <= 'z') return c - 'a';
    if(c >= 'A' && c <= 'Z') return c - 'A' + 26;
    return c - '0' + 52;
}
int ind;
void dfs(int u, int eid) {
    for(int i = 0; i < g[u].size(); i++) {
        int tid = g[u][i].second;
        if(vis[tid]) {
            vis[tid] --;
            dfs(g[u][i].first, tid);
        }
    }
    if(eid) st[ind++] = eid;
}
int main()
{

    scanf("%d", &n);
    for(int i = 0; i < 66 * 66; i++) fa[i] = i;
    for(int i = 1; i <= n; i++) {
        scanf("%s", s[i]);
        int a = getc(s[i][0]);
        int b = getc(s[i][1]);
        int c = getc(s[i][2]);
        int id1 = a * 62 + b;
        int id2 = b * 62 + c;
        v[id1] = 1; v[id2] = 1;
        join(id1, id2);
        if(en[id1][id2] == 0) en[id1][id2] = i, g[id1].push_back(PII(id2, i));
        in[id2]++;
        out[id1]++;
        vis[en[id1][id2]]++;

    }
    int tmp = -1, flag = 0;
    for(int i = 0; i < 66 * 66; i++) {
        if(!v[i]) continue;
        if(tmp == -1) tmp = find(i);
        else {
            if(tmp != find(i)) {
                flag = 1;
                break;
            }
        }
    }
    int cnt = 0, src = -1;
    for(int i = 0; i < 66 * 66; i++) {
        if(!v[i]) continue;
        if(abs(in[i] - out[i]) >= 2) {
            flag = 1;
            break;
        } else if(abs(in[i] - out[i]) == 1) {
            cnt ++;
            if(out[i] - in[i] == 1) src = i;
        }
    }
    if(src == -1) src = tmp;
    if(flag) {
        printf("NO\n");
    } else if(cnt == 0 || cnt == 2) {
        dfs(src, 0);
        printf("YES\n");
        for(int i = ind - 1; i >= 0; i--) {
            int tid = st[i];
            if(i == ind - 1) printf("%s", s[tid]);
            else printf("%c", s[tid][2]);
        }
        printf("\n");
    } else {
        printf("NO\n");
    }
    return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值