我正在尝试仅使用数组(文本是字符数组)来解决一个非常基本的问题,但我在实现一些特定的东西时遇到了问题。
因此,我正在尝试编写一个函数,该函数接受两个字符数组作为参数 - 即文本和模式 - 并返回该模式在文本中出现的次数(文本中有多少个不同的子字符串与该模式匹配)。文本仅由数字和拉丁字母组成。该模式可能包含以下特殊字符(不能出现在文本中):
“*” - 恰好对应一个任意字符; “%” - 对应一位或两位数字(来自十进制数字系统); “@” - 对应一个拉丁字母。
让我们分析一些例子:
文字:“te3t zdrte44q t33t”
模式:“t*%@”
说明:
模式“t*%@”可以匹配“te3a”、“t337q”等子字符串。*对应于任意一个字符,%对应于一位或两位数字,@对应于一个拉丁字母。
在给定文本“te3t zdrte44q t33t”中,模式匹配三个子字符串:“te3t”、“te44q”和“t33t”。
因此,此示例的预期输出为 3。
文字:“aaaaaa”
模式:“aa”
预期产量:5
文字:“123”
模式:“%%”
预期输出:3
我在模式符号 * 和 @ 方面取得了很大进展。它们运行良好,与预期的一个角色完全匹配。然而,我在 % 方面遇到了一些障碍。与其他符号不同,它不仅可以匹配一个符号,有时还可以匹配两个符号,这让我有点头疼。
看,棘手的部分是 % 没有固定长度。它是可变的,因此它可以在一个实例中匹配一个符号,在另一个实例中匹配两个符号。事实证明,弄清楚如何处理这种动态匹配对我来说有点具有挑战性。
如果您对如何解决此问题有任何想法或建议,我非常感谢您的意见。
这是我到目前为止的代码:
#include <iostream>
using namespace std;
const int MAX_SIZE_TEXT = 201;
const int MAX_SIZE_PATTERN = 201;
unsigned getStrLen(char str[])
{
int i = 0, count = 0;
while (str[i] != '\0')
{
i++;
count++;
}
return count;
}
bool isLetter(char ch)
{
return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z');
}
bool isDigit(char ch)
{
return ch >= '0' && ch <= '9';
}
unsigned countMatches(char text[], char pattern[])
{
int textLen = getStrLen(text);
int patternLen = getStrLen(pattern);
int i = 0, j = 0, currentLen = 0, matches = 0;
bool matchedASingleDigit = false;
while (i < textLen)
{
cout << text[i] << "?=" << pattern[j] << endl;
if (text[i] == pattern[j] || pattern[j] == '*'
|| (pattern[j] == '@' && isLetter(text[i])))
{
cout << "(before) i=" << i << "; j=" << j << endl;
currentLen += 1;
i++;
j++;
cout << "(after) i=" << i << "; j=" << j << endl;
cout << "currentLen = " << currentLen << endl;
if (currentLen == patternLen)
{
matches++;
}
}
else if (pattern[j] == '%')
{
/*if (isDigit(text[i]))
{
matchedASingleDigit = true;
currentLen += 1;
i++;
j++;
cout << "(after) i=" << i << "; j=" << j << endl;
cout << "currentLen = " << currentLen << endl;
if (currentLen == patternLen)
{
matches++;
}
}
if (i < textLen - 1 && isDigit[text])*/
}
else
{
i -= currentLen - 1;
j = 0;
cout << "(after) i=" << i << "; j=" << j << endl;
currentLen = 0;
cout << "currentLen = " << currentLen << endl;
}
}
return matches;
}
void testCountingMatches()
{
char text[MAX_SIZE_TEXT];
cin.getline(text, MAX_SIZE_TEXT);
char pattern[MAX_SIZE_PATTERN];
cin.getline(pattern, MAX_SIZE_PATTERN);
unsigned matches = countMatches(text, pattern);
cout << matches << endl;
}
int main()
{
testCountingMatches();
}
对于初学者,您可以使用递归。
unsigned countSingle(char text[], char pattern[])
{
if(text[0] == '\0' || pattern[0]=='\0')
{
return pattern[0]=='\0';
}
if (text[0] == pattern[0] || pattern[0] == '*'
|| (pattern[0] == '@' && isLetter(text[0])))
{
return countSingle(text + 1, pattern + 1);
}
else if(pattern[0] == '%')
{
int percentCount;
for(percentCount = 0; pattern[percentCount] == '%'; percentCount++);
int digitCount = 0;
for(int digitCount = 0; digitCount < percentCount; digitCount++)
{
if(!isDigit(text[digitCount]))
{
return 0;
}
}
text += percentCount - 1;
int matches = 0;
for(int digitCount = 0; digitCount < percentCount; digitCount++)
{
if(isDigit(text[digitCount]))
{
matches += countSingle(text + digitCount, pattern + percentCount);
}
else
{
return matches;
}
}
return matches;
}
return 0;
}
unsigned countMatches(char text[], char pattern[])
{
int textLen = getStrLen(text);
int patternLen = getStrLen(pattern);
int i = 0, j = 0, currentLen = 0, matches = 0;
for(i = 0; i < getStrLen(text); i++){
matches += countSingle(text + i, pattern);
}
return matches;
}
这里我们首先将不同的
i
的计数分解到它自己的函数中,然后我们可以利用递归来推进文本和模式指针。我已使代码尽可能接近您的原始代码。百分号很棘手,正如 @SamVarshavchik 所评论的,如果有 n
连续的百分号,您需要考虑在继续匹配之前从 n
跳到 2n
。这是通过在递归中使用求和来实现的。
现在正如 @n.m.couldbeanAI 评论的那样,要使模式匹配高效并不那么容易。这并不是为了提高效率,只是为了给你一个想法,因为你陷入了困境。