使用T-SQL测试字符串是否是回文


24

我是T-SQL的初学者。我想确定输入字符串是否是回文,如果不是,则输出= 0,如果是,则输出= 1。我仍然在弄清楚语法。我什至没有收到错误消息。我正在寻找不同的解决方案和一些反馈,以更好地理解和了解T-SQL的工作原理,并且变得更好-我仍然是一名学生。

正如我所看到的,关键思想是将最左边和最右边的字符相互比较,检查是否相等,然后将左边的第二个字符与最后一个的第二个字符进行比较,依此类推。我们做一个循环:如果字符彼此相等,我们继续。如果到达末尾,则输出1;否则,输出0。

您能否批评一下:

CREATE function Palindrome(
    @String  Char
    , @StringLength  Int
    , @n Int
    , @Palindrome BIN
    , @StringLeftLength  Int
)
RETURNS Binary
AS
BEGIN
SET @ n=1
SET @StringLength= Len(String)

  WHILE @StringLength - @n >1

  IF
  Left(String,@n)=Right(String, @StringLength)

 SET @n =n+1
 SET @StringLength =StringLength -1

 RETURN @Binary =1

 ELSE RETURN @Palindrome =0

END

我认为自己走在正确的道路上,但距离我们还有很长的路要走。有任何想法吗?


LTRIM(RTRIM(...))空格?
WernerCD '16

Answers:


60

如果使用的是SQL Server,则可以使用REVERSE()函数进行检查?

SELECT CASE WHEN @string = REVERSE(@String) THEN 1 ELSE 0 END AS Palindrome;

包括Martin Smith的评论在内,如果您使用的是SQL Server 2012+,则可以使用IIF()函数:

SELECT IIF(@string = REVERSE(@String),1,0) AS Palindrome;

17

既然有很多解决方案,我将回答您问题的“批判”部分。一些注意事项:我已经修正了一些错字,并指出了我做错的地方。如果我错认为他们是错字,请在评论中提及它,然后我将解释发生的情况。我将指出您可能已经知道的几件事,所以请不要冒犯我。一些评论似乎有些挑剔,但我不知道您的旅行路线,因此必须假设您才刚刚起步。

CREATE function Palindrome (
    @String  Char
    , @StringLength  Int
    , @n Int
    , @Palindrome BIN
    , @StringLeftLength  Int

总是charvarchar定义长度。亚伦· 伯特兰德( Aaron Bertrand)在这里深入讨论。他在谈论,varchar但同样如此charvarchar(255)如果您只想要较短的字符串,或者可能varchar(8000)需要较大的字符串甚至是,我会使用a varchar(max)Varchar是用于可变长度的字符串char,仅用于固定的字符串。由于您不确定使用中传递的字符串的长度varchar。此外,它是binary不是bin

接下来,您不需要将所有这些变量都作为参数。在您的代码中声明它们。仅当您计划将其传入或传出时,才将其放入参数列表中。(您将在最后看到它的外观。)此外,您还有@StringLeftLength,但从不使用它。所以我不会宣布它。

我要做的下一件事是重新格式化一下,使一些事情变得显而易见。

BEGIN
    SET @n=1
    SET @StringLength = Len(@String) -- Missed an @

    WHILE @StringLength - @n >1 
        IF Left(@String,@n)=Right(@String, @StringLength) -- More missing @s
            SET @n = @n + 1 -- Another missing @

    SET @StringLength = @StringLength - 1  -- Watch those @s :)

    RETURN @Palindrome = 1 -- Assuming another typo here 

    ELSE 
        RETURN @Palindrome =0

END

如果您看一下我进行缩进的方式,您会注意到我有这个:

    WHILE @StringLength - @n >1 
        IF Left(@String,@n)=Right(@String, @StringLength)
            SET @n = @n + 1

这是因为喜欢WHILEIF仅影响命令之后的第一行代码。如果要使用BEGIN .. END多个命令,则必须使用一个块。因此,我们得到了解决:

    WHILE @StringLength - @n > 1 
        IF Left(@String,@n)=Right(@String, @StringLength)
            BEGIN 
                SET @n = @n + 1
                SET @StringLength = @StringLength - 1
                RETURN @Palindrome = 1 
            END
        ELSE 
            RETURN @Palindrome = 0

您会注意到,我只在中添加了一个BEGIN .. ENDIF。这是因为即使该IF语句长了多行(甚至包含多个命令),它仍然是一个语句(包含在语句IFELSE部分中执行的所有内容)。

接下来,您都将遇到错误RETURNs。您可以返回变量或文字。您不能设置变量并同时返回它。

                SET @Palindrome = 1 
            END
        ELSE 
            SET @Palindrome = 0

    RETURN @Palindrome

现在我们陷入逻辑。首先让我指出您正在使用的LEFTand RIGHT函数很棒,但是它们将为您提供从请求的方向传入的字符数。假设您通过了“测试”一词。在第一遍中,您将获得此信息(除去变量):

LEFT('test',1) = RIGHT('test',4)
    t          =      test

LEFT('test',2) = RIGHT('test',3)
    te         =      est

显然,这不是您所期望的。您真的想substring代替使用。子字符串不仅可以传递起点,还可以传递长度。这样您将获得:

SUBSTRING('test',1,1) = SUBSTRING('test',4,1)
         t            =         t

SUBSTRING('test',2,1) = SUBSTRING('test',3,1)
         e            =         s

接下来,仅在IF语句的一种情况下递增在循环中使用的变量。将变量增量完全拉出该结构。这将需要一个额外的BEGIN .. END块,但是我确实要删除另一个块。

        WHILE @StringLength - @n > 1 
            BEGIN
                IF SUBSTRING(@String,@n,1) = SUBSTRING(@String, @StringLength,1)
                    SET @Palindrome = 1 
                ELSE 
                    SET @Palindrome = 0

                SET @n = @n + 1
                SET @StringLength = @StringLength - 1
            END

您需要更改WHILE条件以允许进行最后的测试。

        WHILE @StringLength > @n 

最后但并非最不重要的一点是,如果字符数量奇数,我们现在不会测试最后一个字符。例如,如果使用“ ana”,n则未经测试。很好,但是对我来说,我们需要考虑一个字母词(如果您希望将其视为正数)。因此,我们可以通过预先设置该值来实现。

现在我们终于有了:

CREATE FUNCTION Palindrome (@String  varchar(255)) 
RETURNS Binary
AS

    BEGIN
        DECLARE @StringLength  Int
            , @n Int
            , @Palindrome binary

        SET @n = 1
        SET @StringLength = Len(@String)
        SET @Palindrome = 1

        WHILE @StringLength > @n 
            BEGIN
                IF SUBSTRING(@String,@n,1) = SUBSTRING(@String, @StringLength,1)
                    SET @Palindrome = 1 
                ELSE 
                    SET @Palindrome = 0

                SET @n = @n + 1
                SET @StringLength = @StringLength - 1
            END
        RETURN @Palindrome
    END

最后的评论。我通常是格式化的忠实拥护者。它确实可以帮助您了解代码的工作原理,并指出可能的错误。

编辑

正如Sphinxxx所说,我们在逻辑上仍然存在缺陷。一旦我们点击ELSE并设置@Palindrome为0,就没有意义继续了。实际上,那时我们可以做到RETURN

                IF SUBSTRING(@String,@n,1) = SUBSTRING(@String, @StringLength,1)
                    SET @Palindrome = 1 
                ELSE 
                    RETURN 0

鉴于我们现在仅@Palindrome用于“仍然有可能是回文”,因此确实没有意义。我们可以摆脱该变量,在故障RETURN 0和故障(RETURN 1正响应)一直贯穿整个循环时,才将其逻辑切换为短路。您会注意到,这实际上在某种程度上简化了我们的逻辑。

CREATE FUNCTION Palindrome (@String  varchar(255)) 
RETURNS Binary
AS

    BEGIN
        DECLARE @StringLength  Int
            , @n Int

        SET @n = 1
        SET @StringLength = Len(@String)

        WHILE @StringLength > @n 
            BEGIN
                IF SUBSTRING(@String,@n,1) <> SUBSTRING(@String, @StringLength,1)
                    RETURN 0

                SET @n = @n + 1
                SET @StringLength = @StringLength - 1
            END
        RETURN 1
    END

15

您也可以使用数字表方法。

如果您还没有辅助号码表,则可以如下创建一个。这将填充一百万行,因此对于长度不超过200万个字符的字符串来说将是一个不错的选择。

CREATE TABLE dbo.Numbers (number int PRIMARY KEY);

INSERT INTO dbo.Numbers
            (number)
SELECT TOP 1000000 ROW_NUMBER() OVER (ORDER BY @@SPID)
FROM   master..spt_values v1,
       master..spt_values v2 

下面将左边的每个字符与右边的对应伙伴进行比较,如果发现任何差异会短路并返回0。如果字符串是奇数长度,则不检查中间字符,因为这不会改变结果。

DECLARE @Candidate VARCHAR(MAX) = 'aibohphobia'; /*the irrational fear of palindromes.*/

SET @Candidate = LTRIM(RTRIM(@Candidate)); /*Ignoring any leading or trailing spaces. 
                      Could use `DATALENGTH` instead of `LEN` if these are significant*/

SELECT CASE
         WHEN EXISTS (SELECT *
                      FROM   dbo.Numbers
                      WHERE  number <= LEN(@Candidate) / 2
                             AND SUBSTRING(@Candidate, number, 1) 
                              <> SUBSTRING(@Candidate, 1 + LEN(@Candidate) - number, 1))
           THEN 0
         ELSE 1
       END AS IsPalindrome 

如果不确定其工作原理,则可以从下面看到

DECLARE @Candidate VARCHAR(MAX) = 'this is not a palindrome';

SELECT SUBSTRING(@Candidate, number, 1)                       AS [Left],
       SUBSTRING(@Candidate, 1 + LEN(@Candidate) - number, 1) AS [Right]
FROM   dbo.Numbers
WHERE  number <= LEN(@Candidate) / 2; 

在此处输入图片说明

这基本上是与问题中描述的算法相同的算法,但是是以基于集合的方式而不是迭代过程代码来完成的。


12

REVERSE()方法“改良”,即反转字符串的只有一半:

SELECT CASE WHEN RIGHT(@string, LEN(@string)/2) 
                 = REVERSE(LEFT(@string, LEN(@string)/2)) 
            THEN 1 ELSE 0 END AS Palindrome;

如果字符串的字符数为奇数,我不会发生任何奇怪的事情。中间字符不必检查。


@hvd指出,这可能无法在所有归类中正确处理代理对。

@srutzky评论说,它以与REVERSE()方法相同的方式处理补充字符/替代对,因为它们仅在当前数据库的默认Collat​​ion结尾于时才能正常工作_SC


8

不使用REVERSE,这是我马上想到的,但是仍然使用函数1;我将构建类似以下内容的东西。

这一部分只是删除了现有功能(如果已经存在):

IF OBJECT_ID('dbo.IsPalindrome') IS NOT NULL
DROP FUNCTION dbo.IsPalindrome;
GO

这是函数本身:

CREATE FUNCTION dbo.IsPalindrome
(
    @Word NVARCHAR(500)
) 
RETURNS BIT
AS
BEGIN
    DECLARE @IsPalindrome BIT;
    DECLARE @LeftChunk NVARCHAR(250);
    DECLARE @RightChunk NVARCHAR(250);
    DECLARE @StrLen INT;
    DECLARE @Pos INT;

    SET @RightChunk = '';
    SET @IsPalindrome = 0;
    SET @StrLen = LEN(@Word) / 2;
    IF @StrLen % 2 = 1 SET @StrLen = @StrLen - 1;
    SET @Pos = LEN(@Word);
    SET @LeftChunk = LEFT(@Word, @StrLen);

    WHILE @Pos > (LEN(@Word) - @StrLen)
    BEGIN
        SET @RightChunk = @RightChunk + SUBSTRING(@Word, @Pos, 1)
        SET @Pos = @Pos - 1;
    END

    IF @LeftChunk = @RightChunk SET @IsPalindrome = 1;
    RETURN (@IsPalindrome);
END
GO

在这里,我们测试功能:

IF dbo.IsPalindrome('This is a word') = 1 
    PRINT 'YES'
ELSE
    PRINT 'NO';

IF dbo.IsPalindrome('tattarrattat') = 1 
    PRINT 'YES'
ELSE
    PRINT 'NO';

这会将单词的前半部分与单词的后半部分相反(不使用此REVERSE功能)进行比较。此代码可以正确处理奇数和偶数长度的单词。无需遍历整个单词,我们只需获取单词LEFT的前半部分的,然后遍历单词的后半部分即可获得右半部分的相反部分。如果单词是奇数长度,我们将跳过中间字母,因为根据定义,两个“一半”将是相同的。


1-功能可能很慢!


6

不使用REVERSE ...使用递归解决方案总是很有趣;)(我在SQL Server 2012中确实做到了,较早的版本可能对递归有限制)

create function dbo.IsPalindrome (@s varchar(max)) returns bit
as
begin
    return case when left(@s,1) = right(@s,1) then case when len(@s) < 3 then 1 else dbo.IsPalindrome(substring(@s,2,len(@s)-2)) end else 0 end;
end;
GO

select dbo.IsPalindrome('a')
1
select dbo.IsPalindrome('ab')
0
select dbo.IsPalindrome('bab')
1
select dbo.IsPalindrome('gohangasalamiimalasagnahog')
1

6

这是Martin Smith的基于集合的解决方案的 TVF友好内联版本,并附加了一些多余的增强功能:

WITH Nums AS
(
  SELECT
    N = number
  FROM
    dbo.Numbers WITH(FORCESEEK) /*Requires a suitably indexed numbers table*/
)
SELECT
  IsPalindrome =
    CASE
      WHEN EXISTS
      (
        SELECT *
        FROM Nums
        WHERE N <= L / 2
          AND SUBSTRING(S, N, 1) <> SUBSTRING(S, 1 + L - N, 1)
      )
      THEN 0
      ELSE 1
    END
FROM
  (SELECT LTRIM(RTRIM(@Candidate)), LEN(@Candidate)) AS v (S, L)
;

5

只是为了好玩,这是一个带有内存中OLTP功能的SQL Server 2016标量用户定义函数:

ALTER FUNCTION dbo.IsPalindrome2 ( @inputString NVARCHAR(500) )
RETURNS BIT
WITH NATIVE_COMPILATION, SCHEMABINDING
AS
BEGIN ATOMIC WITH (TRANSACTION ISOLATION LEVEL = SNAPSHOT, LANGUAGE = N'English')

    DECLARE @i INT = 1, @j INT = LEN(@inputString)

    WHILE @i < @j
    BEGIN
        IF SUBSTRING( @inputString, @i, 1 ) != SUBSTRING( @inputString, @j, 1 )
        BEGIN
            RETURN(0)
        END
        ELSE
            SELECT @i+=1, @j-=1

    END

    RETURN(1)

END
GO

4

您将遇到的一个主要问题是,任何值大于1 LEFTRIGHT将返回多个字符,而不是该位置的字符。如果您想保持这种测试方法,那么修改它的一种非常简单的方法是

RIGHT(LEFT(String,@n),1)=LEFT(RIGHT(String, @StringLength),1)

这将始终获取左字符串的最右边字符和右字符串的最左边字符。

不过,也许使用一种更简单的方法来检查此问题SUBSTRING

SUBSTRING(String, @n, 1) = SUBSTRING(String, ((LEN(String) - @n) + 1), 1)

请注意,该SUBSTRING索引为1索引,因此为+ 1in ((LEN(String) - @n) + 1)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.