1. 程式人生 > >ACM/ICPC 2018亞洲區預選賽北京賽站網路賽 B. Tomb Raider(二進位制列舉)

ACM/ICPC 2018亞洲區預選賽北京賽站網路賽 B. Tomb Raider(二進位制列舉)

        題意是在n個環形的(首尾相連)字串中找最長公共子序列。

        在比賽的時候因為資料範圍不大,想著去一個一個的暴力把每個串以每個字元為首字元的串都存起來然後去求他們的lcs,然後發現求n個字串的lcs是沒法往下傳遞的,所以就不會了...

        這道題其實和當時的思路是差不多的,就是去列舉每個字串的子序列,但是不用去跑lcs,只需要把這些所有沒出現過的子序列都存起來,如果某個子序列出現的個數是等於n的話就是這n個字串的公共子序列了,然後再對這些存起來的子序列進行一個排序就好了。方法可以用dfs去暴力,這裡我用的是二進位制列舉,需要注意的是在二進位制列舉完子序列後的那個if,是為了防止同一個字串中出現多個相同的子序列的一個判重的操作,其他的不太難理解,只要是二進位制列舉的操作。

AC程式碼:

#include <bits/stdc++.h>
using namespace std;
map<string,int> ma,vis;
vector<string> v;
int n,m;
bool flag;
string s,str,ch;

bool cmp(string a,string b){
  if(a.length() == b.length())return a < b;
  return a.length() > b.length();
}

void init(){
  ma.clear();
  v.clear();
  flag = false;
  m = n;
}

void Fun(int x){
  string index = ch.substr(x);
  index += ch.substr(0,x);
  str = index;
}

int main()
{
  while(~scanf("%d",&n)){
    init();
    while(m--){
      cin>>ch;
      vis.clear();
      int len = ch.length();
      for(int k=0;k<len;k++){
        Fun(k);
        for(int i=1;i<(1<<len);i++){      // 二進位制列舉
          for(int j=0;j<len;j++){
            if(i & (1 << j)){
              s += str[j];
            }
          }
          if(vis[s] == 0){  // 判重
            ma[s] ++;
            vis[s] = 1;
          }
          s.clear();
        }
      }
    }
    for(auto i : ma){       // 遍歷求公共子序列
      if(i.second == n){
        v.push_back(i.first);
        flag = true;
      }
    }
    sort(v.begin(),v.end(),cmp);
    if(flag == false){
      puts("0");
    }
    else{
      cout<<v[0]<<endl;
    }
  }
  return 0;
}