루오 구 문제 해결 --P1019 : 단어 카드 놀이

관련 항목

주제 링크

루오 밸리, https://www.luogu.com.cn/problem/P1019 .

총 승객 마늘, https://nanti.jisuanke.com/t/T2110 .

총 승객 마늘, https://nanti.jisuanke.com/t/T2152 .

내 OJ, http://47.110.135.197/problem.php?id=4209 .

제목 설명

카드 놀이 카드 놀이는 우리가 비슷한 게임의 관용구로 재생하는 데 사용되는 단어입니다, 지금 우리가 단어의 집합을 알고, 가장 긴 "드래곤"을 필요로 시작하는 편지를 주어 (각 단어는이 편지로되어 시작 (가)에서의 "긴"대부분에 두번 나타나는 서로 중복 부분의 일부를 만들어, 짐승 놀라게 예컨대, 기차에 접속하는 경우가 beastonish된다 두 단어에 접속 될 때), 두 개의 추가 부분을 인접 관계에있어서 존재할 수 없다 , 예를 들면, atide의 사이에 접속 될 수 없다.

입력 형식

첫 번째 행동 입력 단일 정수 N (n≤20)는 단어의 수를 나타냅니다, 각 단어가 다음 n 줄은 단일 문자의 마지막 행위의 입력은 처음에 문자 "드래곤"을 나타냅니다. 당신이이 문자 "용"로 시작된다고 가정 할 수 있어야합니다.

출력 형식

그냥 출력의 길이는이 편지 가장 긴 "드래곤"로 시작합니다.

SAMPLE INPUT

5
at
touch
cheat
choose
tact
a

샘플 출력

23

설명

가장 긴 용은 atoucheatactactouchoose입니다.

주제 분석

문제의 의미 분석

단어 주어진 시점에서 가장 긴 용을 찾을 수 있습니다. 각 단어는 드래곤에 두 번 최대에 표시합니다.

예를 들어, 전술의 샘플 데이터를이 단어는 단지 첫 글자와 마지막 글자는 카드 놀이의 규칙에 따라, 당신의 자신을 취할 수, t되어있다. 

샘플 데이터 분석

아래와 같이 우리의 샘플 데이터에 따르면, 상기 관계가 그려 질 수있다 :

가장 긴 단어를 찾기 위해, 위와 같이, DFS는 가능한 모든 DFS에 쓰기로 처음부터 발견 할 수있는 가장 긴 자연적인 과정이다. 만나 DFS 탐색의 요구 사항은 다음을 찾을 수 있습니다 :

22 atoucheatoucheatactact

17 atoucheatouchoose

22 atoucheatactoucheatact

20 atoucheatactouchoose

22 atoucheatactactoucheat

23 atoucheatactactouchoose

10 atouchoose

22 atactoucheatoucheatact

20 atactoucheatouchoose

22 atactoucheatactoucheat

23 atactoucheatactouchoose

13 atactouchoose

22 atactactoucheatoucheat

23 atactactoucheatouchoose

16 atactactouchoose

로 (23)의 최대 길이 이상을 알 수있다.

알고리즘의 생각

1, 판독 데이터.

2, 리더를 찾고.

3, 구조적 변화 관계형 데이터.

4 수도꼭지 모든 통해, 현재 DFS와 수도꼭지, DFS는, 모든 가능한 얻을 가장 긴 데이터를 찾을 수 있습니다.

어떻게 단어 카드 놀이 할 수 있습니다 결정?

필요 최소 오버랩 길이를 찾을 수 있습니다. 카드 놀이 및 다른 워드 abababab은, 우연의 일치 문자, 즉 단어 AB, 카드 놀이는 ababababababab이며,이해야 abababab 예를 들어, 단어가되게합니다.

폭력의 각각의 비교

이 문제는 작은 데이터 량 ≤ N (20)으로 인해, 폭력의 사용과 비교하는 것이 가능하다. 즉, 각각의 비교 문자열 다음 놀이, 즉이 기능을 달성 할 수있는 캐릭터 (B)의 k 번째 위치 :

bool check(const string &a, const string &b, int k)

세대 테이블

관계형 테이블을 유지한다. 위의 그림으로, 우리는 테이블을 생성하는 사용자의 데이터를 읽습니다. 이것은 몇 문자 일치 접속 나중에 설명 문자열 문자열 B를 표현하는 2 차원 배열을 설명한다 고려한다. 이러한 샘플의 입력으로도 지시를 참조하면, 우리는 다음의 표에 도시 된 관계를 구 초기화 문제가없는 두 단어 맞추기는 0이고, 고려할 수 있으며, x> 0이 반복 후행 접속 j 번째 단어에서 i 번째 단어를 나타낸다 X 자.

워드 ...에서 접촉 사기 고르다 재치
...에서 0 1 0 0 1
접촉 0 0 0
사기 0 1 0 0 1
고르다 0 0 0 0 0
재치 0 1 0 0 1

무슨 아직 테이블에 의미합니까? A [I] [J]를 몇 j 번째 문자열 일치 뒤에 접속 i 번째 문자열을 나타낸다. 이러한 [1] [2] = 2, 스트링 터치 속이 연결 문자열 반복 문자 놀이 toucheat의 단어의 길이. 5 (접촉 길이) + 5 (치트 길이의 끝을 나타낸다 ) - 2- (오버랩 길이) = 8.

코드 구현 아이디어

1 TT으로 표시되는 최소 워드 길이에서, i 번째와 j 번째의 단어를 얻었다.

도 2에서, I, J == 경우, 워드, 다음 저장 TT 설명한다. 그들이 할 수 없기 때문에

3, 개방 루프, 시작부터 종료까지 K 1 TT. 동일한 문자 K는, 애프터 연결된 단어 나 한 경우 K 캐릭터의 정면에서 촬영 한 i 번째의 단어의 j 번째 단어 K 문자 전면 뒤로 찍은 J를 번째 긴 단어가있을 것이다 K 문자가 반복됩니다.

4주기의 종료 후, 무엇을 포함 관계를 결정한다. 계산 TT 문자와 동일한 크기를 반복하는 경우, 상기 관계를 설명.

다음과 같이 코드가 실행된다 :

    //找龙头和构建重复字符表
    vector<int> head;
    for (int i=0; i<n; i++) {
        if (ch==word[i].at(0)) {
            head.push_back(i);
        }
        //第i个单词后面接上第j个单词,将重复 k 个字符
        for (int j=0; j<n; j++) {
            int tt = min(word[i].length(), word[j].length());
            string tmp1, tmp2;
            for (int k=1; k<=tt; k++) {
                //word[i]逆序获取
                tmp1 = word[i].substr(word[i].length()-k, k);
                //word[j]正序获取
                tmp2 = word[j].substr(0, k);
                if (tmp1==tmp2) {
                    relation[i][j]=k;
                    break;
                }
            }

            //包含关系
            if (relation[i][j]==tt) {
                relation[i][j]=0;
            }
        }
    }

AC 참조 코드

#include <iostream>
#include <string>
#include <vector>

using namespace std;

const int MAXN = 20+2;
const int MAXUSED = 2;
string word[MAXN];//单词
int used[MAXN];//单词已经使用次数
int relation[MAXN][MAXN];
int sum;//当前龙单词长度
int ans;//最长的长度
int n;//
string dragon; 

//用第 i 个单词开始接龙
void dfs(int i) {
    //标志位,如果发生遍历说明数据构造没有结束
    bool flag = true;
    //遍历
    for (int j=0; j<n; j++) {
        //第 j 个单词没有超过使用限制
        //并且第 j 个单词可以接着第 i 个单词后面
        if (used[j]<MAXUSED && relation[i][j]>0) {
            used[j]++;
            sum += (word[j].length()-relation[i][j]);
            dragon += word[j].substr(relation[i][j], word[j].length()-relation[i][j]);
            dfs(j);
            sum -= (word[j].length()-relation[i][j]);
            used[j]--;
            dragon = dragon.substr(0, dragon.length()-(word[j].length()-relation[i][j]));
            flag = false;
        }
    }

    if (true == flag) {
        //cout << dragon << " " << sum << endl;
        ans = max(ans, sum);
    }
}

int main() {
    cin >> n;
    for (int i=0; i<n; i++) {
        cin>>word[i];
    }
    char ch;
    cin >> ch;

    //找龙头和构建重复字符表
    vector<int> head;
    for (int i=0; i<n; i++) {
        if (ch==word[i].at(0)) {
            head.push_back(i);
        }
        //第i个单词后面接上第j个单词,将重复 k 个字符
        for (int j=0; j<n; j++) {
            int tt = min(word[i].length(), word[j].length());
            string tmp1, tmp2;
            for (int k=1; k<=tt; k++) {
                //word[i]逆序获取
                tmp1 = word[i].substr(word[i].length()-k, k);
                //word[j]正序获取
                tmp2 = word[j].substr(0, k);
                if (tmp1==tmp2) {
                    relation[i][j]=k;
                    break;
                }
            }

            //包含关系
            if (relation[i][j]==tt) {
                relation[i][j]=0;
            }
        }
    }

    int index;
    vector<int>::iterator it;
    for (it=head.begin(); it<head.end(); it++) {
        index = *it;
        used[index]++;
        sum += word[index].length();
        dragon = word[index];
        dfs(index);
        sum -= word[index].length();
        used[index]--;
    }

    cout << ans << endl;

    return 0;
}

 

추신

글 설명은 너무 피곤 TMD이 보고서를 앞뒤로, 지속적으로 수정 알고리즘 사고 부분의 4+ 시간을 썼다보고한다. 문제에 대한 해결책은 분석의 대상이 가장 중요하는 문제의 해결에보고하지만, 코드를 볼 수 없습니다.

게시 된 235 개 원래 기사 · 원 찬양 289 · 조회수 1,070,000 +

추천

출처blog.csdn.net/justidle/article/details/104878323