【问题标题】:Check if a sequence is composed of two identical sequences检查一个序列是否由两个相同的序列组成
【发布时间】:2021-12-01 10:45:46
【问题描述】:

问题是找到一个 1

请帮我找到一个更聪明的算法来真正解决这个问题。为了更好地理解(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]] << " ";
    }
}

【问题讨论】:

  • 我认为对于子序列如何交织没有额外的限制?
  • 对如何进行交织没有任何限制,我只需对序列进行排序,看看它是否包含每个值的偶数。除此之外,还需要对每个子序列的形成方式进行一些限制。
  • @JerryCoffin 这是一个序列而不是排列。因此 a, b, b, a, c, c 无效,但 a, b, a, c, b, c 有效,因为您可以通过这种方式从左到右删除 a, b, c剩下的就是 a、b、c。
  • 您提到您还想找到原始子序列。模棱两可的时候呢?例如aabbaabb 可以由aabbabab 组成。
  • @JerryCoffin 正如 maraca 已经说过的那样,不需要约束,因为我们无法更改子序列中术语的顺序

标签: c++ algorithm text lexical-analysis


【解决方案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] && 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 个问题。但我不得不将堆栈大小增加到 100M。您可能希望将其重写为迭代解决方案而不是递归解决方案。

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

【讨论】:

  • 它看起来不错,它工作得很快吗?
  • 我尝试了您的方法,也实施了您的建议。可悲的是,它太慢了。
  • @Kangaroo976 您是否先尝试不进行优化?这可能是更多的开销。需要优化else子句。问题可能是针对相同数字在开始时出现很多次但问题无法解决的情况。所以我们能做的就是记住不可解的数字。例如。 aaaaaabccb 会检查 aaa 10 次和 aa 15 次。
  • @Kangaroo976 “太慢了”为了什么?
  • @Kangaroo976 添加了另一个版本
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-03-25
  • 1970-01-01
  • 1970-01-01
  • 2012-03-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多