【问题标题】:Looking for repeating patterns in integer numbers寻找整数中的重复模式
【发布时间】:2019-02-13 15:34:33
【问题描述】:

我试图找到整数中的重复模式。为此,我使用单独存储每个数字的向量。我想把整个事情写成一个打印出数字重复模式的函数。这些数字大多是 12 位数字。

一个例子:
电话号码是 057 987 051 2057
我想要打印出来的功能:
057
05
它也应该只打印出最长的循环模式。我目前停留在如何在 C++ 中实现整个事情。我也想知道是否有任何库支持我正在寻找的功能。

提前致谢!

编辑:

for (vector<int>::iterator it = m_number.begin(); it != m_number.end(); ++it) {
    int follower = *(it + 1);
    for (vector<int>::iterator jt = it + 1; jt != m_number.end(); ++jt) {
        if (*jt == *it) {
            if (*(jt + 1) == follower)
                cout << *jt;
            }
        }
    cout << endl;
    }

这是我迄今为止尝试过的。这显然还不起作用,但我希望它能让您了解我想要该功能做什么。我尝试使用两个遍历 m_number 向量的 for 循环(其中包含我要检查的整数。)。

【问题讨论】:

  • 你好。看来你是新来的。通常要获得帮助,您必须向您展示您尝试了什么、失败的原因以及您的期望。
  • 另一个思路:std::rotate 数字向量并找到共同前缀。
  • 如果一定要高效,可以考虑使用后缀树:en.wikipedia.org/wiki/Suffix_tree

标签: c++


【解决方案1】:

你可能会使用类似的东西:

std::set<std::vector<int>> GetRepetitingPatterns(const std::vector<int>& v)
{
    std::set<std::vector<int>> res;
    for (std::size_t size = v.size() - 1; size != 0; --size) {
        std::set<std::vector<int>> s;

        for (std::size_t i = 0; i + size != v.size() + 1; ++i) {
            auto [it, inserted] = s.emplace(v.begin() + i, v.begin() + i + size);
            if (!inserted) {
                res.insert(*it);
            }
        }
#if 1 // Longest only
        if (!res.empty()) {
            return res;   
        }
#endif
    }
    return {};
}

Demo

对于变化(所有/最长和最小尺寸): Demo

【讨论】:

  • 感谢您的回答,但演示似乎对我不起作用
  • 需要 C++17,链接已更改。
  • 谢谢!但是这个实现只打印出“057”,但模式“05”也会出现,应该打印出来
  • Other demo 可以使用参数自定义..
  • @TedLyngmo:确实,忘记了它不能重复它自己的一部分。固定代码。
【解决方案2】:

这是第一种方法:

for (size_t i = 0; i < m_numbers.size(); i++) {
    // Look for patterns starting everywhere in the following numbers
    for (size_t j = i+1; j < m_numbers.size(); j++) {
      // Pattern storing
      std::vector<int> pattern;

      // Condition to stop the search
      bool valid_pattern = true;

      // Variable for the while loop
      int offset = 0;
      while (valid_pattern && j+offset < m_numbers.size()) {
        // If the numbers are equal, continue the pattern, else, stop the search
        if(m_numbers[i+offset] == m_numbers[j+offset]){
          pattern.push_back(m_numbers[i+offset]);
        } else {
          valid_pattern = false;
        }
        offset++;
      }

      // If the pattern has at least two numbers, output it to the console
      if(pattern.size() > 1){
        for (size_t idx = 0; idx < pattern.size(); idx++) {
          std::cout << pattern[idx];
        }
        std::cout << std::endl;
      }
    }
  }

【讨论】:

    【解决方案3】:

    您可以使用unordered_map 开头。

    #include <iostream>
    #include <iomanip>
    #include <unordered_map>
    
    int main() {
        std::string str = "0579870512057";
        // a map of substrings and the number of times they appear
        std::unordered_map<std::string, size_t> pm;
    
        for(auto a=str.begin(); a!=str.end()-1; ++a) {
            for(auto b=a+1; b!=str.end(); ++b) {
                // std::string(a, b+1) creates the substring from your iterators
                // the unordered_map::operator[] creates a key, value pair
                // if the key doesn't exist (value is default constructed)
                // ++ increases the value by 1
                ++pm[ std::string(a, b+1) ];
            }
        }
        // display the number of times each substring appears (if it appeared more than once)
        // and display the length (size) of each
        for(auto& p : pm) {
            if(p.second>1)
                std::cout << std::setw(3) << p.second << " "
                          << std::setw(12) << p.first.size() << " " << p.first << "\n";
        }
    }
    

    稍微复杂一点的方法是创建一个自定义set,以保持循环模式从最长到最短排序,但它的优点是您会知道结果中的第一个模式将是最长的。除了找到的模式之外,它也不会复制原始输入。

    #include <iostream>
    #include <set>
    
    struct comp { // a custom comparison class to sort the result
        bool operator()(const std::string& a, const std::string& b) const {
            if(b.size()==a.size()) return a < b; // lowest "value" when length is equal
            else return b.size() < a.size();     // but longest pattern goes first
        }      // note  b  before  a  when comparing sizes
    };
    
    using string_pattern_set = std::set<std::string, comp>; // custom set type using "comp"
    
    string_pattern_set get_recurring_patterns(const std::string& str) {
        string_pattern_set sorted_result;
    
        for(size_t len=str.size()-1; len>0; --len) {
            auto end = str.end()-len;
            for(auto pos=str.begin(); pos<end; ++pos) {
                for(auto check=pos+1; check<=end; ++check) {
                    if(std::equal(pos, pos+len, check)) {
                        sorted_result.emplace(pos, pos+len);
                        break;
                    }
                }
            }
        }
        return sorted_result;
    }
    
    int main() {
        string_pattern_set res = get_recurring_patterns("0579870512057");
        if(res.size())
            std::cout << "the longest recurring pattern is "
                      << res.begin()->size() << " char(s)\n";
        // display all patterns from longest to shortest
        for(auto& str : res) std::cout << str << "\n";
    }
    

    如果您真的担心内存消耗,第三种选择是根本不存储重复出现的字符串,而是存储匹配子字符串的 beginend 迭代器。这种方法适用于预期重复模式较长且丰富的情况。

    #include <iostream>
    #include <set>
    #include <algorithm>
    #include <iterator>
    
    // type to store begin and end iterators for matching patterns
    using strits = std::pair<std::string::const_iterator, std::string::const_iterator>;
    
    struct comp { // a custom comparison class to sort the result
        bool operator()(const strits& a, const strits& b) const {
            if((b.second-b.first)==(a.second-a.first)) // lowest "value" when length is equal
                return std::lexicographical_compare(a.first, a.second, b.first, b.second);
            else // but longest pattern goes first
                return (b.second-b.first) < (a.second-a.first);
        }      // note  b  before            a  when comparing sizes
    };
    
    using string_pattern_set = std::set<strits, comp>; // custom set type using "comp"
    
    template<bool LONGEST_ONLY = false, bool BIG_STRING = false>
    string_pattern_set get_recurring_patterns(const std::string& str) {
        string_pattern_set sorted_result;
    
        for(size_t len=str.size()-1; len>0; --len) {
            auto end = str.end()-len;
            for(auto begin=str.begin(); begin<end; ++begin) {
                if constexpr (BIG_STRING) {
                    // skip patterns already found to be recurring.
                    // in C++20, use set::contains instead
                    if(sorted_result.find(strits(begin, begin+len))!=sorted_result.end())
                        continue;
                }
                for(auto check=begin+1; check<=end; ++check) {
                    if(std::equal(begin, begin+len, check)) {
                        sorted_result.emplace(begin, begin+len);
                        break;
                    }
                }
            }
            if constexpr (LONGEST_ONLY)
                if(!sorted_result.empty()) break;
        }
        return sorted_result;
    }
    
    int main() {
        string_pattern_set res = get_recurring_patterns("0579870512057");
    
        if(res.size()) {
            const auto& [begin, end] = *res.begin();
            std::cout << "the longest recurring pattern is "
                      << (end - begin)<< " char(s)\n";
        }
    
        // display all patterns from longest to shortest
        for(const auto& [begin, end] : res) {
            std::copy(begin, end, std::ostream_iterator<char>(std::cout));
            std::cout << "\n";
        }
    }
    

    【讨论】:

    • 请注意,在长字符串的情况下,O(N^2) 的内存使用可能会非常糟糕。
    • 12 个字符长将创建最多 78 个地图条目。我不会太担心。
    • 感谢您的回复!我有一个问题,第 8 行中的“size_t”到底是做什么的?此外,我以前从未使用过地图,所以我对地图的概念很陌生,因此我也很难弄清楚“p.second”和“p.first”的作用。
    • @Xetron size_t 是一种无符号整数类型,适用于您在内存中拥有的任意长度和索引的事物,但这样做太过分了。即使是 unsigned char 也可以用作计数器。 p.first:当您遍历 unordered_map 时,您会得到 std::pair&lt;key, value&gt;p.first 是键(本例中为子字符串),p.second 是值(本例中为计数)。
    • @Xetron 如果某些东西相当快速和简单并且能够完成手头的任务,那么就没有必要让它复杂化。 12个数字是最大值,即使突然变成双倍也可以,如果问题变得更复杂,假设你有100万个数字,那么它需要一个更复杂的解决方案。
    猜你喜欢
    • 1970-01-01
    • 2018-03-07
    • 2012-06-21
    • 2021-10-04
    • 1970-01-01
    • 2015-08-31
    • 1970-01-01
    • 2011-10-22
    • 1970-01-01
    相关资源
    最近更新 更多