AcWing 1165. 单词环 (SPFA求正环&&二分哈希)

整理的算法模板: ACM算法模板总结(分类详细版)

 

我们有 nn 个字符串,每个字符串都是由 a∼za∼z 的小写英文字母组成的。

如果字符串 AA 的结尾两个字符刚好与字符串 BB 的开头两个字符相匹配,那么我们称 AA 与 BB 能够相连(注意:AA 能与 BB 相连不代表 BB 能与 AA 相连)。

我们希望从给定的字符串中找出一些,使得它们首尾相连形成一个环串(一个串首尾相连也算),我们想要使这个环串的平均长度最大。

如下例:

ababc
bckjaca
caahoynaab

第一个串能与第二个串相连,第二个串能与第三个串相连,第三个串能与第一个串相连,我们按照此顺序相连,便形成了一个环串,长度为 5+7+10=225+7+10=22(重复部分算两次),总共使用了 33 个串,所以平均长度是 223≈7.33223≈7.33。

输入格式

本题有多组数据。

每组数据的第一行,一个整数 nn,表示字符串数量;

接下来 nn 行,每行一个长度小于等于 10001000 的字符串。

读入以 n=0n=0 结束。

输出格式

若不存在环串,输出”No solution”,否则输出最长的环串的平均长度。

只要答案与标准答案的差不超过 0.010.01,就视为答案正确。

数据范围

1≤n≤1051≤n≤105

输入样例:

3
intercommunicational
alkylbenzenesulfonate
tetraiodophenolphthalein
0

输出样例:

21.66

 

 思路:

直接把每个字符串的编号进行建图数据量太庞大(如果是1e5个aaaa的话那么要建1e10条边,直接gg);

所以可以将字符串的前两个字符按哈希看成一个点,同理后两个字符也一样;然后一个字符串就可以看成前后两个点连成的一条边了,这样一共也就有26*26=676个点,然后最多676*676条边;

处理完所有字符串之后,二分mid(mid为要 求的平均值);用spfa算法看图中有没有正环,;如果有正环,说明此时的mid小了,反之亦然;

spfa算法中要对边进行处理:

判正环求最长路,判负环求最短路;

注意当mid=0的时候都没有正环的话,那么一定是没有正环;

(然后我吐了.....)玄学优化,当访问点的总次数达到2~5倍的总点数时,就可直接return 说明有环;

#include <bits/stdc++.h>
using namespace std;
const int N=700,M=1e5+7;
int e[M],h[M],ne[M],w[M],idx,n,cnt[N];
double dis[N];
bool st[N];
void add(int a,int b,int c)
{
    e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++;
}
bool spfa(double mid)
{
    memset(dis,-0x3f,sizeof dis);
    memset(st,false,sizeof st);
    memset(cnt,0,sizeof cnt);
    queue<int> q;
    for(int i=0;i<676;i++) q.push(i),st[i]=true;
    int count=0;
    while(q.size())
    {
        int t=q.front();
        st[t]=false;
        q.pop();
        for(int i=h[t];i!=-1;i=ne[i])
        {
            int j=e[i];
            if(dis[j]<dis[t]+w[i]-mid)
            {
                dis[j]=dis[t]+w[i]-mid;
                cnt[j]=cnt[t]+1;
                if(++count>4*N) return true;
                if(cnt[j]>=N) return true;
                if(!st[j])
                {
                    q.push(j);
                    st[j]=true;
                }
            }
        }
    }
    return false;
}
int main()
{
    while(cin >>n&&n)
    {
        idx=0;
        memset(h,-1,sizeof h);
        for(int i=0;i<n;i++)
        {
            string s;
            cin >>s;
            int len=s.size();
            if(len>=2)
            {
                int l=(s[0]-'a')*26+s[1]-'a',r=(s[len-2]-'a')*26+s[len-1]-'a';
                add(l,r,len);
            }
        }
        if(!spfa(0)) puts("No solution");
        else
        {
            double l=0,r=1000;
            while(r-l>1e-4)
            {
                double mid=(l+r)/2;
                if(spfa(mid)) l=mid;
                else r=mid;
            }
            printf("%lf\n",l);
        }
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值