所以基本上,我可能会看到一些字符串:“嘿,这是一个字符串*这个字符串很棒97 * 3 = 27 *这个字符串很酷”。
但是,这个字符串可能很大。我试图从字符串中删除所有星号,除非星号似乎表示乘法。效率在这里有点重要,我很难找到一个好的算法来从中删除所有非乘法星号。
为了确定星号是否用于乘法,我显然可以检查它是否夹在两个数字之间。
因此,我以为我可以做一些像(伪代码)的事情:
wasNumber = false
Loop through string
if number
set wasNumber = true
else
set wasNumber = false
if asterisk
if wasNumber
if the next word is a number
do nothing
else
remove asterisk
else
remove asterisk
然而,^在一个巨大的字符串上是丑陋和低效的。你能想到用C ++实现这个目标的更好方法吗?
另外,我怎么能真正检查一个单词是否是一个数字?它被允许为小数。我知道有一个函数来检查一个字符是否是一个数字......
答案 0 :(得分:4)
功能齐全的代码:
#include <iostream>
#include <string>
using namespace std;
string RemoveAllAstericks(string);
void RemoveSingleAsterick(string&, int);
bool IsDigit(char);
int main()
{
string myString = "hey this is a string * this string is awesome 97 * 3 = 27 * this string is cool";
string newString = RemoveAllAstericks(myString);
cout << "Original: " << myString << "\n";
cout << "Modified: " << newString << endl;
system("pause");
return 0;
}
string RemoveAllAstericks(string s)
{
int len = s.size();
int pos;
for(int i = 0; i < len; i++)
{
if(s[i] != '*')
continue;
pos = i - 1;
char cBefore = s[pos];
while(cBefore == ' ')
{
pos--;
cBefore = s[pos];
}
pos = i + 1;
char cAfter = s[pos];
while(cAfter == ' ')
{
pos++;
cAfter = s[pos];
}
if( IsDigit(cBefore) && IsDigit(cAfter) )
RemoveSingleAsterick(s, i);
}
return s;
}
void RemoveSingleAsterick(string& s, int i)
{
s[i] = ' '; // Replaces * with a space, but you can do whatever you want
}
bool IsDigit(char c)
{
return (c <= 57 && c >= 48);
}
顶级概述:
代码搜索字符串,直到遇到*
。然后,它查看*
之前和之后的第一个非空白字符。如果两个字符都是数字,则代码确定这是乘法运算,并删除星号。否则,它会被忽略。
如果您需要其他详细信息,请参阅此帖子的修订历史记录。
重要说明:
0
或大于len
的索引IsDigit()
函数)。 (我的代码检查'*',这是一个逻辑操作。)然而,发布的一些建议非常糟糕。不要使用正则表达式来检查字符是否为数字。既然你在问题中提到了效率,而且我没有足够的代表点来评论其他答案:
检查'0''1''2'...的switch语句意味着每个不是数字的字符必须经过10次逻辑操作。在char
映射到int
之后,请充分尊重,只需检查边界(char <= '9' && char >= '0')
答案 1 :(得分:3)
您可以从实施慢速版本开始,它可能比您想象的要快得多。但是,让我们说它太慢了。这是一个优化问题。低效率在哪里?
现在,这是“删除星号”部分,这是一个问题。这里要注意的关键点是你不需要复制字符串:你可以实际修改它,因为你只是删除元素。
尝试在实现它之前尝试直观地运行它。
保留两个整数或迭代器,第一个说明你当前读你的字符串,第二个说你当前写你的字符串。由于你只删除了东西,所以读取的东西总是在写作之前。
如果您决定保留当前字符串,则只需逐个推进每个整数/迭代器,然后相应地进行复制。如果您不想保留它,只需提前读取字符串即可!然后你只需要删除你删除的星号数量的字符串。复杂性只是O(n),没有使用任何额外的缓冲区。
另请注意,如果这样编写,您的算法会更简单(但等效):
wasNumber = false
Loop through string
if number
set wasNumber = true
else
set wasNumber = false
if asterisk and wasNumber and next word is a number
do nothing // using my algorithm, "do nothing" actually copies what you intend to keep
else
remove asterisk
答案 2 :(得分:3)
我发现你的小问题很有趣,我写了一个小而简单的函数(并测试了),这个函数可以在std::string
上完成。你去吧:
// TestStringsCpp.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <string>
#include <iostream>
using namespace std;
string& ClearAsterisk(string& iString)
{
bool bLastCharNumeric = false;
string lString = "0123456789";
for (string::iterator it = iString.begin(); it != iString.end() ; ++it) {
switch (*it) {
case ' ': break;//ignore whitespace characters
case '*':
if (bLastCharNumeric) {
//asterisk is preceded by numeric character. we have to check if
//the following non space character is numeric also
for (string::iterator it2 = it + 1; it2 != iString.end() ; ++it2) {
if (*it2 != ' ') {
if (*it2 <= '9' && *it2 >= '0') break;
else iString.erase(it);
break; //exit current for
}
}
}
else iString.erase(it);;
break;
default:
if (*it <= '9' && *it >= '0') bLastCharNumeric= true;
else bLastCharNumeric = false; //reset flag
}
}
return iString;
}
int _tmain(int argc, _TCHAR* argv[])
{
string testString = "hey this is a string * this string is awesome 97 * 3 = 27 * this string is cool";
cout<<ClearAsterisk(testString).c_str();
cin >> testString; //this is just for the app to pause a bit :)
return 0;
}
它将与您的示例字符串完美配合,但如果您有这样的文本它将失败:"this is a happy 5 * 3day menu"
因为它仅检查'*'之后的第一个非空格字符。但坦率地说,我不能想象你会在句子中使用这种结构的很多案例。
HTH,
JP。
答案 3 :(得分:0)
正则表达式不一定会更高效,但它会让你依赖别人来进行字符串解析和操作。
就个人而言,如果我担心效率,我会实现你的伪代码版本,同时限制不必要的内存分配。我甚至可能mmap
输入文件。我非常怀疑你会比这更快。