[SDOI2007]游戏

题意:接龙,对于一个串 i i ,如果串j所有中字符的出现次数比 i i 有且只有一个位置大1,那么就能接上

算法一:暴力 DP D P

因为每次长度都会多 1 1

所以考虑按照长度DP

fi=max(fj)+1,leni=lenj+1 f i = m a x ( f j ) + 1 , l e n i = l e n j + 1 且合法

输出方案就记录一下转移点

复杂度 O(26lensumlensumlen1) O ( 26 ∗ ∑ l e n s u m l e n ∗ s u m l e n − 1 )

sumlen s u m l e n 表示长度是 len l e n 的串的个数

可以知道极限复杂度是 O(26n24) O ( 26 ∗ n 2 4 )

不知道这种DP能不能优化

由于是暴力,所以代码就随便写了一下

#include<bits/stdc++.h>
#define fp(i,a,b) for(register int i=a,I=b+1;i<I;++i)
#define fd(i,a,b) for(register int i=a,I=b-1;i>I;--i)
#define go(u) for(register int i=fi[u],v=e[i].to;i;v=e[i=e[i].nx].to)
#define file(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
char sr[1<<21];int C=-1;
const int N=1e4+5,M=105,inf=1e9;
typedef int arr[N];
struct da{int l,c[26];}a[N];
int n,m,k,ans,pos,L=inf,R;arr pr,f;
char s[N][M];vector<int>len[M];
inline bool cmp(const da&a,const da&b){return a.l<b.l;}
void dfs(int u){
    if(!u)return;
    dfs(pr[u]);
    puts(s[u]+1);
}
inline bool chk(int u,int v){
    int p=0;
    fp(i,0,25){
        if(a[u].c[i]==a[v].c[i])continue;
        if(a[u].c[i]<a[v].c[i])return 0;
        if(a[u].c[i]>a[v].c[i]+1)return 0;
        p^=1;
    }return p;
}
int main(){
    #ifndef ONLINE_JUDGE
        file("s");
    #endif
    while(~scanf("%s",s[++n]+1)){
        a[n].l=strlen(s[n]+1);
        fp(i,1,a[n].l)++a[n].c[s[n][i]-'a'];
    }--n;
    fp(i,1,n)len[a[i].l].push_back(i),cmin(L,a[i].l),cmax(R,a[i].l);
    fp(l,L,R){
        for(int i:len[l])f[i]=1;
        if(!len[l-1].size())continue;
        for(int i:len[l]){
            for(int j:len[l-1]){
                if(chk(i,j)){
                    if(f[i]<f[j]+1)
                        f[i]=f[j]+1,pr[i]=j;
                }
            }
            if(f[i]>ans)ans=f[i],pos=i;
        }
    }
    printf("%d\n",ans);
    dfs(pos);
return 0;
}

算法二:字符串 hash h a s h + DAG D A G 最长路

对于每个字符串,你增加他的某一个字符,看看得到的新字符串是不是存在

存在就连边,得到的一定是一个 DAG D A G ,然后跑最长路就好了

至于怎么判断是不是存在,你把 cnt[26] c n t [ 26 ] 这个数组 hash h a s h 一下就好了

#include<bits/stdc++.h>
#define fp(i,a,b) for(register int i=a,I=b+1;i<I;++i)
#define fd(i,a,b) for(register int i=a,I=b-1;i>I;--i)
#define go(u) for(register int i=fi[u],v=e[i].to;i;v=e[i=e[i].nx].to)
#define file(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
char sr[1<<21];int C=-1;
const int N=1e4+5,M=1e7;
typedef int arr[N];
struct eg{int nx,to;}e[N];
struct da{int l,c[26];}a[N];
int n,m,ce,ans,pos,ha[M];arr pr,f,fi,in,S,d;
char s[N][105];queue<int>q;
inline void add(int u,int v){e[++ce]={fi[u],v},fi[u]=ce,++in[v];}
inline int get(int*s){
    int tp=0;
    fp(i,0,25)tp=(233ll*tp%M+s[i])%M;
    return tp;
}
int main(){
    #ifndef ONLINE_JUDGE
        file("s");
    #endif
    while(~scanf("%s",s[++n]+1));--n;
    fp(i,1,n){
        a[i].l=strlen(s[i]+1);
        fp(j,1,a[i].l)++a[i].c[s[i][j]-'a'];
        ha[get(a[i].c)]=i;
    }
    fp(i,1,n){
        fp(j,0,25){
            ++a[i].c[j];
            int v=get(a[i].c);
            if(ha[v])add(i,ha[v]);
            --a[i].c[j];
        }
    }
    fp(i,1,n)if(!in[i])q.push(i),d[i]=1;
    while(!q.empty()){int u=q.front();q.pop();go(u)d[v]=d[u]+1,--in[v],pr[v]=u,!in[v]?q.push(v):void();}
    fp(i,1,n)if(d[i]>ans)ans=d[pos=i];
    while(pos)S[++S[0]]=pos,pos=pr[pos];
    printf("%d\n",ans);
    fd(i,S[0],1)puts(s[S[i]]+1);
return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值