假设您正在从字符流中读取,当您读取第一次出现的回文时,该函数应该返回。
回文的长度应该是偶数。
时间复杂度的要求是O(N)。
例子:
- 第一个字符:4
- 第二个字符:1
- 第三个字符:3
- 第四个字符:3
- 第 5 个字符:1
- 第6个字符:4,返回
假设您正在从字符流中读取,当您读取第一次出现的回文时,该函数应该返回。
回文的长度应该是偶数。
时间复杂度的要求是O(N)。
例子:
当你读到第一个字符时返回,这是一个单字母回文。
您需要对Manacher's Algorithm稍作修改。它允许您在线性时间内找到字符串中的所有回文。关于算法的事情,它实际上从左到右进行,在需要时“使用”新字符。所需的修改是您需要读取新字符,仅当您尝试访问它时。
停止,如果你找到回文,那会一直追溯到流的开头。
在大多数情况下,应该在线性时间 O(n) 内运行的一种近似解决方案是使用滚动哈希。
您跟踪字符串的哈希值及其反向。每次读取一个新字符时,都可以更新 O(1) 中的两个哈希值。然后比较两个哈希值,如果它们相等,则比较字符串及其保留值。如果这也是相等的,退出并且你得到一个回文。
例如,一个非常简单的滚动哈希函数是 hash(ck c(k-1).. c0) = (p^k*ck + p^(k - 1) * c(k - 1) + ... + c0 ) mod m 其中 p 是奇素数。
所以开始:
p = 17 // or your lucky prime number
m = 10^9 // ...
hash = 0
hash_rev = 0
str = ''
str_rev = ''
p_pow = 1
while True
read c
append c to str
prepend c to str_rev
hash = (hash * p + c) mod m
hash_rev = (hash_rev + p_pow * c) mod m
p_pow = p_pow * p
if hash == hash_rev
if str == str_rev
found str to be the first palindrome
为了使其更快,您可以将哈希和 hash_rev 清除为 32 位整数并选择 m = 2^32。然后你可以忽略 (mod m) 操作。
这个(未经测试的 C#)代码可以做到,但我认为它可能是 O(n^2)。任何人都可以确认/否认吗?
main()
{
string str = "";
char c1;
char c2;
while(true)
{
//has to be even, always get two chars at a time
c1 = GetCharFromStream();
c2 = GetCharFromStream();
str += c1 + c2;
if(isPalindrome(str))
{
Console.WriteLine(str);
return;
}
}
}
bool isPalindrome(string str)
{
if (str.Length % 2 != 0)
throw new InvalidArgumentException("str");
int first = 0;
int last = str.Length - 1;
while(first <= last)
{
if(str[first] != str[last])
return false;
first++;
last--;
}
return true;
}
使用 Hashset 怎么样?
假设输入流是1221
.
Hashset = empty;
if(hashset does not contain the inputelement)
{
add to hashset. // 12 will be in the hashset. //413 for your example.
}
else
{
delete from Hashset. // First deletes 2 and then 1. // First deletes 3, then 1 then 4.
if(hashset is empty)
return true; //Hashset is empty. return true.
}
这是我的看法。它从字符串的当前中间扫描每个新符号上的累积字符串,因此如果当前字符串不是回文,它将快速失败。
class PalindromeDetector {
private static class DetectorImpl {
final ArrayList<Character> string = new ArrayList<Character>(32);
boolean addSymbol(char symbol) {
string.add(symbol);
return check();
}
private boolean check() {
if (string.size() < 2)
return false;
int lowBound = string.size() / 2 - 1;
int hiBound = lowBound + 1;
while (lowBound >= 0 && string.get(lowBound) == string.get(hiBound)) {
lowBound --; hiBound ++;
}
return lowBound == -1;
}
}
static boolean isPalindrome(String s) {
DetectorImpl detector = new DetectorImpl();
int index = 0;
while (index < s.length())
if (detector.addSymbol(s.charAt(index++)))
return true;
return false;
}
}
这是代码的单元测试:
@Test
public void test() {
assertFalse(PalindromeDetector.isPalindrome("4"));
assertTrue(PalindromeDetector.isPalindrome("44"));
assertFalse(PalindromeDetector.isPalindrome("4343"));
assertTrue(PalindromeDetector.isPalindrome("4334"));
assertFalse(PalindromeDetector.isPalindrome("41331"));
assertTrue(PalindromeDetector.isPalindrome("413314"));
}
好的,因为我的第一个答案具有O(n^2)
时间复杂性,所以这是另一个,O(n)
按要求。
class PalindromeDetector {
private static class DetectorImpl {
int firstHalfSum, secondHalfSum;
int size = 0, tensPower = 1;
boolean add(int v) {
if (size % 2 == 1) {
firstHalfSum = firstHalfSum + (secondHalfSum / tensPower) * tensPower;
secondHalfSum = (secondHalfSum % tensPower) * 10 + v;
if (firstHalfSum == secondHalfSum)
return true;
} else {
secondHalfSum = secondHalfSum * 10 + v;
}
size ++;
if (size % 2 == 0)
tensPower *= 10;
return false;
}
}
static boolean isPalindrome(String s) {
if (s == null || s.length() == 0)
return false;
int val = Integer.parseInt(s);
int tensPower = s.length() == 1 ? 1 : (int) (Math.pow(10, s.length() - 1));
DetectorImpl detector = new DetectorImpl();
while (tensPower > 0) {
int digit = val / tensPower;
if (detector.add(digit))
return true;
val = val % tensPower;
tensPower /= 10;
}
return false;
}
}
这是单元测试:
@Test
public void test() {
assertFalse(PalindromeDetector.isPalindrome("4113"));
assertTrue(PalindromeDetector.isPalindrome("3443"));
assertTrue(PalindromeDetector.isPalindrome("473374"));
assertTrue(PalindromeDetector.isPalindrome("413314"));
}
答案假设输入由十进制数字组成,但可以很容易地扩展为字母数字输入(假设英文字母 + 10 位为我们提供基于 36 的数字系统)。
这个解决方案(在 Haskell 中)依赖于对偶数长度回文在其中心包含重复字符的观察。当我们从输入流中读取字符时,我们会测试这样的对,当找到一个时,我们会播种一个新的候选答案。对于每个候选回文,我们还保留一个“剩余要匹配的字符”列表,并且在读取每个新字符时,我们将其与该列表进行匹配——如果没有匹配,候选者将被丢弃。当匹配列表为空时,已找到解决方案。请注意,虽然每个候选者都维护自己的匹配列表,但这些都只是同一列表的后缀,因此在 Haskell 中,这些都将共享空间,并且即使有很多候选者,它们也不会占用超过 O(n) 的空间。
在答案中心只有一个配对字符并且因此没有“误报”候选者的最佳情况下,时间复杂度为 O(n) - 每个字符检查不超过两次。对于具有许多错误开始的输入字符串,即“abbbbbbbbbbbbba”,我不确定时间复杂度 - 它可能不再是 O(n) 但它比 O(n^2) 更好,因为没有候选人能活得更久min(k, n-k)
而不是k
候选人中心的位置。
filter_map::(a -> Maybe a)->[a]->[a]
filter_map op = foldr (maybeCons . op) []
where maybeCons Nothing ys = ys
maybeCons (Just y) ys = y:ys
-- Return the first prefix of the input argument that
-- is an even-length palindrome.
prefix_palindrome::(Eq a)=>[a]->[a]
prefix_palindrome (x:xs) = search [x] [] xs
where search seen ([]:_) _ = seen
search (s:seen) candidates (x:xs) | x == s = search seen' (candidates' ++ [seen]) xs
| otherwise = search seen' candidates' xs
where seen' = (x:s:seen)
candidates' = filter_map extend candidates
where extend (c:cs) | c == x = Just cs
| otherwise = Nothing