一、问题描述(来源:计蒜客)
二、思路一
从字符串的第二个字母开始循环,如果后续有相同的字母,则将后面相同的字母删除,该删除字母后面的字母全部往前移一位。一轮循环完后,再将开头的字母删除(如果该字母有重复)。如果后面没有找到与这个字母相同的字母,就结束循环,打印字符串的第一个字母;如果检测到这个字符串是空的,则打印no。
注:这种方法比较复杂,代码运行耗时较长,而且有几个地方不注意容易出错。注释里有详细说明。
代码实现:
#include<stdio.h>
#include<stdlib.h>
int main()
{
char str[100000];
int i, j, flag=0;
gets(str);
//i从1开始递增,让str[i]与str[0]比较
for (i = 1; i < strlen(str); i++)
{
//如果字母str[i]与字母str[0]相同,
//则将str[i]后面的字母全部往左移一位
if (str[i] == str[0])
{
for (j = i; j < strlen(str)-1; j++)
{
str[j] = str[j + 1];
}
//将str最后一个元素赋值为\0,\0为字符串结束标识符
str[strlen(str) - 1] = '\0';
printf("%s\n", str);
//flag用于标记是否有字母和第一个字母相同
//如果有赋值为1,没有就为默认值0
flag = 1;
//因为在当前循环结束后,i的值会加1,所以,这里将i减1后,下一次循环i的值不变
//为什么要这一步?
//这里是考虑到如果有两个相同的字母相邻且和第一个字母相同,例如str="abcaabc"
//当i=3时,检测到str[3]==str[0]==a,这时将后三个字母abc左移一位,最后一个赋值\0,
//此时str="abcabc",如果不对i进行处理的话,下一次循环i的值就为4,进而比较的是str[4]和str[0],
//即比较a和b了,原来字符串中两个相邻的a中的第二个a就被跳过比较了。
i = i - 1;
}
//如果第一个字母之后有与其相同的字母(flag==1)
//而且已经比较到字符串的最后一个字母了,这时与第一个字母相同的字母都已经删除了
//然后需要将第一个字母也删除掉。
if (flag == 1 && str[i + 1] == '\0')
{
//从第二个字母开始到最后一个字母全部向左移一位,最后一个字母赋值为\0
for (j = 0; j < strlen(str) - 1; j++)
{
str[j] = str[j + 1];
}
str[strlen(str) - 1] = '\0';
//初始化标记
flag = 0;
//因为第一个字母已经不是原来的字母了,为了继续删除其他相同的字母,
//还需要将第一个之后的字母逐一与之比较,这里将i赋值为0,
//执行完当前循环后i加1变成1了,而str[1]正好是字符串的第二个元素,
//这样又可以从第二个字母开始,将后续的字母逐一与第一个字母进行比较。
i = 0;
printf("%s\n", str);
}
//如果比较到最后一个字母还是没有与第一个字母相同的字母
//这时就退出循环,字符串str的第一个字母就是第一个只出现一次的字母
else if (flag == 0 && str[i + 1] == '\0')
{
break;
}
}
//如果字符串str的第一个字母不为空,那么打印第一个字母
//如果字符串str的第一个字母为空,说明整个字符串中没有不重复的字母,
//经过上面的一系列操作后使str变成了空字符串,此时打印no。
if (str[0] != '\0')
printf("%c", str[0]);
else
printf("no");
return 0;
}
测试结果:
三、思路二
从第一个字母开始循环,如果后面有相同的字母,将其替换成一个特殊字符,一轮循环后,如果有与循环开始的字母相同的字母,还需要将循环开始的那个字母替换成这个特殊字符,如果没有找到相同的字母,则结束循环,打印这个字母。
注:因为题目已经说明字符串中全部是小写字母,所以可以将重复的全部替换成一个特殊字符。
代码实现:
#include<stdio.h>
#include<stdlib.h>
int main()
{
char str[100000];
int n, i, j, flag;
gets(str);
n = strlen(str); //字符串str的长度
for (i = 0; i < n-1; i++) //外循环n-1轮
{
//如果str[i]不是字符*,那么需要将其和后面的字母进行比较
//如果str[i]是字符*,那么就不需要将其和后续字母进行比较了
if (str[i] != '*')
{
//初始化标记,默认值为0
//flag为0表示字母str[i]后面没有与其相同的字母,
//这时内循环结束一次后不需要将str[i]赋值为字符*;
//flag为1表示字母str[i]后面的又与其相同的字母,
//这时内循环结束一次后需要将str[i]赋值为字符*;
flag = 0;
for (j = i + 1; j < n; j++) //内循环从str[i]后面一个字符开始
{
//如果str[j]不是字符*,且字母str[i]和字母str[j]相同
//将str[j]赋值为*,并改变标记flag的值为1
if (str[j] != '*' && str[i] == str[j])
{
str[j] = '*';
flag = 1;
printf("%s\n",str);
}
}
//如果一轮内循环结束后,判断flag的值是否为1,
//如果flag为1,表示字符串中字母str[i]后有与其相同的字母,
//这时将str[i]赋值为*
if (flag == 1)
{
str[i] = '*';
printf("%s\n", str);
}
}
}
//printf("%s\n",str);
//遍历字符串str的每个元素
for (i = 0; i < n; i++)
{
//如果第i个元素不是字符*,说明这个字母在该字符串中只出现了一次
if (str[i] != '*')
{
printf("%c", str[i]);
break; //只需要打印第一个只出现一次的字母,因此要退出循环
}
//如果遍历到最后一个元素,说明前面的元素都是字符*,
//这时,如果最后一个元素还是字符*的话,说明没有不重复的字母。
if (i == n - 1 && str[i- 1] == '*')
{
printf("no");
}
}
return 0;
}
测试结果:
四、思路三
题目中已说明字符串中只包含小写字母。小写字母一共只有26个,如果定义一个26个元素的数组,分别存储a~z在字符串中出现的次数,那么只需要找出这个数组中值为1的第一个元素的下标,然后将其对应的字母打印出来就可以了,如果没有值为1的元素,那么打印no。
注:这种方法最好理解,也更容易实现。
代码实现:
#include<stdio.h>
#include<stdlib.h>
int main()
{
char str[100000];
int i, array[26] = { 0 };
gets(str);
for (i = 0; i < strlen(str); i++)
{
array[str[i] - 'a']++;
}
for (i = 0; i < 26; i++)
{
if (array[i] == 1)
{
printf("%c",'a'+i);
break;
}
else if (i == 25 && array[i] != 1)
{
printf("no");
}
}
return 0;
}
测试结果:
五、思路三问题更正
非常感谢网友“ForTres”提出的问题。思路三中的代码无法做到“找到第一个只出现一次”,比如字符串序列 “ abzcabd”,'z','c','d'都是下标为1,返回的结果是'c'。在原基础上做了修改,代码如下,具体的思路请看代码中的注释。
#include<stdio.h>
#include<stdlib.h>
int main()
{
char str[100000];
//定义二维数组,第一行记录a~z出现的次数
//第二行记录a~z在字符串str中出现的位置下标
int i, array[2][26] = { 0 };
gets(str);
//minIndex记录最小的位置下标,初值为字符串str长度
int minIndex = strlen(str);
//index记录第一个只出现一次的字符在array数组中的位置下标
int index = -1;
for (i = 0; i < strlen(str); i++)
{
//数组array第一行计数
array[0][str[i] - 'a']++;
//数组array第二行记录最新出现的位置下标
array[1][str[i] - 'a'] = i;
}
for (i = 0; i < 26; i++)
{
//如果数组array中第一行中的第i列为1,则对应的字符自出现了一次
if (array[0][i] == 1)
{
//如果当前只出现一次的字符在字符串str中的位置下标小于minIndex
if (array[1][i] < minIndex){
//将该字符出现的位置下标赋值给minIndex
minIndex = array[1][i];
//记录这个字符在数组array中的位置下标
index = i;
}
}
}
//如果index没有变化,仍然等于-1,说明字符串str中没有只出现一次的字符
if (index != -1){
printf("%c", 'a' + index);
}
else{
printf("no");
}
return 0;
}