检测分组单词的最佳方法

时间:2010-02-11 19:54:17

标签: c++ algorithm

如果对于单词中的每个字母,该字母的所有出现恰好形成一个连续序列,则对单词进行分组。换句话说,没有两个相等的字母被一个或多个不同的字母分开。

给定vector<string>返回分组字数。

例如:

{“ab”,“aa”,“aca”,“ba”,“bb”}

返回 4

此处,“aca”不是分组的单词。

我的快速而肮脏的解决方案:

int howMany(vector <string> words) {
  int ans = 0;
  for (int i = 0; i < words.size(); i++) {
       bool grouped = true;
  for (int j = 0; j < words[i].size()-1; j++)
      if (words[i][j] != words[i][j+1])
         for (int k = j+1; k < words[i].size(); k++)
           if (words[i][j] == words[i][k])
              grouped = false;
           if (grouped) ans++;
       }
   return ans;
 }

我想为同样的问题找到更好的算法。

7 个答案:

答案 0 :(得分:2)

尝试以下方法:

bool isGrouped( string const& str )
{
  set<char> foundCharacters;
  char currentCharacter='\0';

  for( int i = 0 ; i < str.size() ; ++i )
  {
    char c = str[i];
    if( c != currentCharacter )
    {
      if( foundCharacters.insert(c).second )
      {
        currentCharacter = c;
      }
      else
      {
        return false;
      }
    }
  }
  return true;
}

答案 1 :(得分:1)

只考虑一个词,这是一个O(n log n)破坏性算法:

std::string::iterator unq_end = std::unique( word.begin(), word.end() );
std::sort( word.begin(), unq_end );
return std::unique( word.begin(), unq_end ) == unq_end;

编辑:对unique的第一次调用会减少连续字母到单个字母的运行。对sort的调用将相同的字母组合在一起。第二次调用unique会检查sort是否形成任何新的连续字母组。如果确实如此,则不得对该单词进行分组。

与其他人发布的优势在于它不需要存储 - 尽管这不是一个优势。

这是替代算法的简单版本,也只需要O(1)存储(是的,也经过测试):

if ( word.empty() ) return true;
bitset<CHAR_MAX+1> symbols;
for ( string::const_iterator it = word.begin() + 1; it != word.end(); ++ it ) {
    if ( it[0] == it[-1] ) continue;
    if ( symbols[ it[0] ] ) return false;
    symbols[ it[-1] ] = true;
}
return ! symbols[ * word.rbegin() ];

请注意,您需要进行少量修改才能使用ASCII以外的字符。 bitset来自标题<bitset>

答案 2 :(得分:1)

您可以使用某种类型的集合(最好使用O(1)插入和查找时间)。

每次遇到与前一个字符不同的字符时,请检查该字符集是否包含该字符。如果是,则您的匹配失败。如果没有,请将其添加到集合中并继续。

答案 3 :(得分:0)

这可能在每个单词的两个循环中起作用:

1)循环计算出现的不同符号的数量。 (这将需要额外的存储空间,最多等于字符串的长度 - 可能是某种哈希值。)

2)循环计算符号n与符号n + 1不同的次数。

如果这两个值之间没有恰好相同,则该单词不会被分组。

答案 4 :(得分:0)

这是一种每个单词有两个循环的方法,除了其中一个循环直到单词长度,但直到字母大小。最坏的情况是O(N L s),其中N =单词数,L =单词长度,s =字母大小:

for each word wrd:
{
  for each character c in the alphabet:
  {
    for each letter i in wrd:
    {
      let poz = last position of character c in wrd. initially poz = -1
      if ( poz == -1 && c == wrd[i] )
         poz = i;
      else if ( c == wrd[i] && poz != i - 1 )
         // definitely not grouped, as it's separated by at least one letter from the prev sequence
    }
  }
  // grouped if the above else condition never executed
}

基本上,检查字母表中的每个字母是否都不存在,或者只出现在那些字母的一个子字符串中。

答案 5 :(得分:0)

    public static Boolean isGrouped( String input )
    {
        char[] c = input.ToCharArray();
        int pointer = 0;
        while ( pointer < c.Length - 1 )
        {
            char current = c[pointer];
            char next = c[++ pointer];
            if (   next != current && 
                 ( next + 1 ) != current && 
                 ( next - 1 ) == current 
               ) return false; 
        }
        return true;
    }

(C#但校长适用)

答案 6 :(得分:0)

这是一个多行,详细,正则表达式来匹配失败:

    (?:         # Non capturing group of ...
      (\S)\1*   # One or more of any non space character (capured).
    )
    (?!         # Then a position without
      \1        # ... the captured character
    ).+         # ... at least once.
    \1          # Followed by the captured character.

或更小:

"(?:(\S)\1*)(?!\1).+\1"

我只是假设C ++有一个regexp实现,它可以在Python中运行,也可以在Perl和Ruby中运行。