Проверьте, состоит ли последовательность из двух одинаковых последовательностей

#c #algorithm #text #lexical-analysis

Вопрос:

Проблема состоит в том, чтобы найти для 1<=n Другими словами, если исходная последовательность была создана путем переплетения двух копий некоторой подпоследовательности. Если это так, мы должны дополнительно распечатать исходную подпоследовательность. Моя идея состояла в том, чтобы наивно разделить каждое другое число того же рода на две подпоследовательности. Так, например, первая подпоследовательность получит 1-ю 5, 3-ю 5 и 1-ю 4, а вторая-2-ю 5, 4-ю 5 и 2-ю 4. Я думал, что последовательность не может быть выражена как комбинация двух подобных копий, если в ней нечетное число некоторых чисел. Однако весь подход ошибочен и очень редко дает хорошие ответы;

Пожалуйста, помогите мне найти более умный алгоритм, который действительно решит проблему. Моя реализация наивного подхода для лучшего понимания (C ):

 #include<iostream> #include<string> #include<algorithm> #include<vector>  using namespace std;  int main() {  ios_base::sync_with_stdio(0);  cin.tie(0);   int n;  cin >> n;   vector<short> arr(n);  for (int i = 0; i < n;   i)  {  cin >> arr[i];  }   vector<vector<unsigned short>> positions(10001, vector<unsigned short>(0));   for (int i = 0; i < n;   i)  {  positions[arr[i]].push_back(i);  }   vector<unsigned short> out;   for (int i = 0; i <= 10000;   i)  {  if (positions[i].size() % 2)   {  cout << "NO" << "n";  return 0;  }   for (int j = 0; j < positions[i].size(); j  = 2)  {  out.push_back(positions[i][j]);  }  }   sort(out.begin(), out.end());   cout << "YES" << "n";  for (int i = 0; i < out.size();   i)  {  cout << arr[out[i]] << " ";  } }  

Комментарии:

1. Я так понимаю, что нет никаких дополнительных ограничений на то, как могут быть переплетены подпоследовательности?

2. Не имея никаких ограничений на то, как можно выполнить переплетение, я бы просто отсортировал последовательность и посмотрел, содержит ли она четное число каждого значения. Более того, требуется некоторое ограничение на то, как формируется каждая подпоследовательность.

3. @JerryCoffin Это последовательность, а не перестановка. Таким образом, a, b, b, a, c, c будет недействительным, но a, b, a, c, b, c будет действительным, потому что вы можете удалить a, b, c слева направо таким образом, чтобы у вас остались a, b, c.

4. Вы упомянули, что также хотите найти исходную подпоследовательность. А как насчет того, когда это двусмысленно? например aabbaabb , может состоять из или aabb , или abab .

5. @JerryCoffin как уже сказал марака, никаких ограничений не требуется, так как мы не можем изменить порядок терминов в подпоследовательностях

Ответ №1:

Вы можете решить эту проблему с помощью обратного отслеживания. Также возможна некоторая оптимизация. Вы знаете, что первое число должно быть первым номером последовательности. Таким образом, все буквы между первым и вторым появлением этого числа должны следовать в определенной последовательности. Аналогично для последнего числа в последовательности. Кроме того, вы можете проверить, осталось ли достаточно чисел для завершения последовательности.

 public int[] solve(int[] arr) {  if (arr.length % 2 != 0)  return null;  int[] solution = new int[arr.length / 2];  if (solve(arr, 0, 0, solution))  return solution;  return null; }  private boolean solve(int[] arr, int i, int j, int[] solution) {  if (i == j) {  solution[i] = arr[i   j];  return solve(arr, i   1, j, solution);  } else if (i == solution.length) {  for (int k = 0; k   i   j < arr.length; k  )  if (arr[k   i   j] != solution[j   k])  return false;  return true;  } else {  for (int k = 0; i   k <= solution.length; k  ) {  if (arr[i   j   k] == solution[j] amp;amp; solve(arr, i   k, j   1, solution))  return true;  if (i   k < solution.length)  solution[i   k] = arr[i   j   k];  }  return false;  } }  

Это проверка только слева направо, а не с обоих концов. Другие возможные улучшения:

  • Проверьте, является ли количество для каждого числа четным в начале
  • Следите за количеством чисел, чтобы раньше знать, когда число больше не может входить в первую последовательность, потому что в нем уже есть половина из них

Это код Java, но его можно легко перевести на C . Массивы передаются по ссылке. Я протестировал несколько простых случаев, и для них это сработало.

С помощью запоминания я мог бы легко решить проблемы до 50000 за несколько мс. Но мне пришлось увеличить размер стека до 100 метров. Возможно, вам захочется переписать это в итеративное решение вместо рекурсивного.

 public static int[] solve(int[] arr) {  if (arr.length % 2 != 0)  return null;  int[] solution = new int[arr.length / 2];  if (solve(arr, 0, 0, solution, new Node(null, null)))  return solution;  return null; }  private static boolean solve(int[] arr, int i, int j, int[] solution, Node node) {  if (node.checked)  return false;  if (i == j) {  if (node.children.containsKey(arr[i   j]))  node = node.children.get(arr[i   j]);  else  node = new Node(node, arr[i   j]);  if (node.checked)  return false;  solution[i] = arr[i   j];  Node n = new Node(node, solution[i]);  if (solve(arr, i   1, j, solution, n))  return true;  n.checked = true;  return false;  } else if (i == solution.length) {  for (int k = 0; k   i   j < arr.length; k  )  if (arr[k   i   j] != solution[j   k]) {  node.checked = true;  return false;   }  return true;  } else {  Node n = node;  for (int k = 0; i   k <= solution.length; k  ) {  if (arr[i   j   k] == solution[j] amp;amp; solve(arr, i   k, j   1, solution, n))  return true;  if (i   k < solution.length) {  if (n.children.containsKey(arr[i   j   k]))  n = n.children.get(arr[i   j   k]);  else  n = new Node(n, arr[i   j   k]);  if (n.checked)  return false;  solution[i   k] = arr[i   j   k];  }  }  node.checked = true;  return false;  } }  private static class Node {  public boolean checked;  public Map<Integer, Node> children = new HashMap<>();  public Node(Node parent, Integer value) {  if (parent != null)  parent.children.put(value, this);  } }  

Комментарии:

1. это выглядит хорошо, работает ли это прилично быстро?

2. Я попробовал ваш подход, а также реализовал ваши предложения. К сожалению, это слишком медленно.

3. @Kangaroo976 вы пробовали сначала без оптимизации? Возможно, это скорее накладные расходы. Необходимо оптимизировать предложение else. Проблема, вероятно, возникает в тех случаях, когда одно и то же число появляется много раз в начале, но проблема неразрешима. Так что мы могли бы запомнить неразрешимые числа. Например, aaaaaabccb проверил бы aaa 10 раз и aa 15 раз.

4. @Kangaroo976 «слишком медленно» для чего?

5. @Kangaroo976 добавлена еще одна версия