字符串旋转

字符串操作与链表翻转技术详解
本文详细介绍了字符串前缀移位、链表指定翻转及单词顺序翻转的技术,包括原地处理和牺牲空间的方法。通过具体实例解析算法实现,并提供了不同场景的应用示例,旨在深入理解字符串与链表操作的高效策略。

题目描述
给定一个字符串,要求把字符串前面的若干个字符移动到字符串的尾部,如把字符串“abcdef”前面的2个字符'a'和'b'移动到字符串的尾部,使得原字符串变成字符串“cdefab”。请写一个函数完成此功能

对于这个问题,我们首先想到的就是暴力移位法,就是将字符串的第一个字符保存起来,其余的往前挪动一格,然后将保存起来的字符放到字符串的最末尾,然后循环执行这个过程。得到代码:

void LeftShiftOne(char *buff, int n) {
  char temp = buff[0];
  int i;
  for(i=1;i<n;i++) {
    buff[i-1] = buff[i];
  }
  buff[n-1] = temp;
}

void LeftRotateString(char *buff, int n, int m) {
  while(m--) {
    LeftShiftOne(buff, n);
  }

}

当然,我们也可以牺牲一点空间,重新开辟一个等大小的字符串,然后将字符从旧字符串中依次按要求移动过来。

然而我们采取一种原地处理的办法。

将一个字符串分成X和Y两个部分,在每部分字符串上定义反转操作,如X^T,即把X的所有字符反转(如,X="abc",那么X^T="cba"),那么就得到下面的结论:(X^TY^T)^T=YX,显然就解决了字符串的反转问题。

例如,字符串 abcdef ,若要让def翻转到abc的前头,只要按照下述3个步骤操作即可:

1. 首先将原字符串分为两个部分,即X:abc,Y:def;
2. 将X反转,X->X^T,即得:abc->cba;将Y反转,Y->Y^T,即得:def->fed。
3. 反转上述步骤得到的结果字符串X^TY^T,即反转字符串cbafed的两部分(cba和fed)给予反转,cbafed得到defabc,形式化表示为(X^TY^T)^T=YX,这就实现了整个反转。

得到下面代码:

#include <stdio.h>

void Reverse(char * buff, int begin, int end) {

  char temp;

  while(end > begin) {
    temp = buff[begin];
    buff[begin] = buff[end];
    buff[end] = temp;
    begin++;
    end--;
  }

}

void LeftRotateString(char *buff, int n, int m) {

  Reverse(buff, 0, m-1);
  Reverse(buff, m, n-1);
  Reverse(buff, 0, n-1);

}


举一反三:
1、链表翻转。给出一个链表和一个数k,比如,链表为1→2→3→4→5→6,k=2,则翻转后2→1→6→5→4→3,若k=3,翻转后3→2→1→6→5→4,若k=4,翻转后4→3→2→1→6→5,用程序实现。

2、编写程序,在原字符串中把字符串尾部的m个字符移动到字符串的头部,要求:长度为n的字符串操作时间复杂度为O(n),空间复杂度为O(1)。 例如,原字符串为”IloveSYSU”,m=7,输出结果为:”SYSUIlove”。

3、单词翻转。输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变,句子中单词以空格符隔开。为简单起见,标点符号和普通字母一样处理。例如,输入“I am a student.”,则输出“student. a am I”。


1、链表的指定翻转:

我们可以用上面题目的反向思维,即是先将连链表的前k个元素全部挪动到链表的末尾,然后将整个链表翻转过来,恰好得到了我们所需要的结果。

#include <stdio.h>
#include <malloc.h>
#include <string.h>

typedef struct nodeh {
  int data;
  struct node * next;
  unsigned int size;
} nodeh;

typedef struct node {
  int data;
  struct node * next;
} node;

nodeh * push(nodeh * head, int c, int k);
nodeh * delete(nodeh * head, int k);
nodeh * roate_list(nodeh * head, int k);

nodeh * build_list_from_string(nodeh * head, char * s);
void list_to_string(nodeh * head);

int main() {
  nodeh * head = NULL;
  char s[1000];
  int k;
  scanf("%s", s);
  scanf("%d", &k);
  head = build_list_from_string(head, s);
  head = roate_list(head, k);
  list_to_string(head);
  return 0;
}

// k = 0 means insert before head
nodeh * push(nodeh * head, int c, int k) {
  
  if(head == NULL) {
    if(k == 0) {
      head = (nodeh *)malloc(sizeof(nodeh));
      head->data = c;
      head->next = NULL;
      head->size = 1;
      return head;
    } else {
    return NULL;
    }
  }

  unsigned int size = head->size;
  int i;

  if(size < k) {
    return NULL;
  }

  node * element;
  element = (node *)malloc(sizeof(node));
  if(element == NULL) {
    printf("element allocation failed!");
    return NULL;
  }
  element->data = c;
  element->next = NULL;

  if (k == 0) {
    int temp_c = head->data;
    head->data = c;
    element->data = temp_c;
    
    element->next = head->next;
    head->next = element;
    head->size++;
    return head;
  }

  if(k == 1) {
    element->next = head->next;
    head->next = element;
    head->size++;
    return head;
  }

  node * positioner = head->next;
  
  i = 2;
  while(i++ < k) {
    positioner = positioner->next;
  }
  element->next = positioner->next;
  positioner->next = element;

  head->size++;
  return head;
}

nodeh * delete(nodeh * head, int k) {
  if(head == NULL) {
    return NULL;
  }

  unsigned int size = head->size;

  if (k > size) {
    return head;
  }
  
  if (size == 1) {
    free(head);
    return NULL;
  }

  node * to_delete = head->next;
  
  if(k == 0) {
    head->data = head->next->data;
    head->next = head->next->next;
    free(to_delete);
    head->size--;
    return head;
  }

  node * positioner = head->next;
  to_delete = to_delete->next;

  int i = 1;
  
  while(i++ < k) {
    to_delete = to_delete->next;
    positioner = positioner->next;
  }

  positioner->next = to_delete->next;
  free(to_delete);
  head->size--;
  return head;
}

nodeh * build_list_from_string(nodeh * head, char * s) {
  if(s == NULL) {
    head = NULL;
    return;
  } else if(strlen(s) == 0) {
    head = NULL;
    return;
  }
  
  const char Delimiter = '-';
  
  int i, temp = 0;
  for(i = 0; i <strlen(s); i++) {
    if(s[i] >= '0' && s[i] <= '9') {
      temp *= 10;
      temp += s[i] - '0';
    } else if(s[i] == Delimiter) {
      head = push(head, temp, head == NULL ? 0 : head->size);
      temp = 0;
    }
  }
  head = push(head, temp, head == NULL ? 0 : head->size);
}

void list_to_string(nodeh * head) {
  node * positioner = NULL;

  if(head == NULL) {
    return;
  }

  printf("%d : ", head->size);
  printf("%d", head->data);

  for(positioner = head->next; positioner != NULL; positioner = positioner->next) {
    printf("->%d", positioner->data);
  }
  printf("\n");
}

nodeh * roate_list(nodeh * head, int k) {
  if (head == NULL || k <= 0 || k > head->size) {
    return head;
  }

  int temp;
  while(k--) {
    temp = head->data;
    head = delete(head, 0);
    head = push(head, temp, head == NULL ? 0 : head->size);
  }

  node * head_next = head->next;
  node * saver = (node *)head;
  node * positioner = saver->next;
  node * new_head = NULL;
  
  while(positioner != NULL) {
    saver->next = new_head;
    new_head = (node *)saver;
    saver = positioner;
    positioner = positioner->next;
  }
  saver->next = new_head;
  new_head = (node *)saver;
  
  saver = new_head->next;
  temp = new_head->data;
  free(new_head);

  nodeh * true_new_head;
  true_new_head = (nodeh *)malloc(sizeof(nodeh));
  true_new_head->next = saver;
  true_new_head->data = temp;
  true_new_head->size = head->size;

  temp = head->data;
  free(head);

  node * tail = malloc(sizeof(node));
  tail->next = NULL;
  tail->data = temp;
  
  head_next->next = tail;

  return true_new_head;
}


2、程序也是一个应用题目很典型的例子,我们只需要把前面(n-k)个字符往后移动就相当于把后k个字符往前移动了。

#include <stdio.h>
#include <string.h>

void ReverseString(char* s,int from,int to) {
    while (from < to) {
        char t = s[from];
        s[from++] = s[to];
        s[to--] = t;
    }
}

void LeftRotateString(char * s, int k, int length) {
  ReverseString(s, 0, k-1);
  ReverseString(s, k, length-1);
  ReverseString(s, 0, length-1);
}

int main() {
  char s[1000];
  int k;
  scanf("%s%d", s, &k);
  LeftRotateString(s, strlen(s) - k, strlen(s));
  printf("%s\n", s);
  return 0;
}


3、这一个也是一个很好的变式,要输出题目要求的字符串,其实就是将每个空格隔开的字符串旋转,然后再将整体旋转:

#include <stdio.h>
#include <string.h>

void ReverseString(char* s,int from,int to) {
    while (from < to) {
        char t = s[from];
        s[from++] = s[to];
        s[to--] = t;
    }
}

void WordsString(char * s, int length) {
  int f = 0, t;
  int i;
  for(i = 0; i < length;i++) {
    if(s[i] == ' ') {
      t = i-1;
      ReverseString(s, f, t);
      f = i+1;
    }
  }
  ReverseString(s, f, length-1);
  ReverseString(s, 0, length-1);
}

int main() {
  char s[1000];
  gets(s);
  WordsString(s, strlen(s));
  printf("%s\n", s);
  return 0;
}


出处:The-Art-Of-Programming-By-July

https://github.com/julycoding/The-Art-Of-Programming-By-July

转载于:https://my.oschina.net/yejq08/blog/415472

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值