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);
        }
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章